Эта тема на forum.dklab.ru


Advanced Guest: ООП и пхп и магазин, помогите пожалуйста разобраться
Здравствуйте,
Прекрасно понимаю что пощу ньюбский вопрос, но если кто-то еще помнит, иногда в самом начале нужен правильный толчек что бы разобраться:)
Почитал про ООП и пхп, решил магазин сделать с помощью ООП. Но честно говоря не вполне догоняю куда его можно прикрутить.

Магазин, значит есть товары.
Допустим я делаю объект товар, да?
Тогда описываю к нему функции add/delete и т.д..
Но у товара допустим я хочу сделать что бы было возможно добавлять доп. свойства (допустим цвет, вес и т.д.) произвольное количество произвольно называющиеся.
Заранее их я не могу прописать как переменные с разными именами свойств.
По идее нужно что-то вроде add_property функции, но вопрос в том что она должна делать?
Если хранить просто набор свойств как массив в объекте "товар", то это получается опять какой-то не ООПшный подход и непонятно почему это точно так же не сделать на функциях. А как сделать реализацию свойств с точки зрения ООП правильно я не понимаю.
Это первый вопрос

Второй вот в чем. допустим я хочу взять набор товаров в какой-то категории. если я просто делаю выборку из бд в файле, а потом создаю объекты на каждый товар, то это не оопшный подход какой-то опять. А как сделать эту выборку с соблюдением ООП не понимаю. Допустим я создам объект "список", но по уму он как-то должен быть связан с объектами? ДОлжен иметь возможность содержать в себе НЕСКОЛЬКО объектов, и как это реализовывать? Опять же массивом? не по ООПшному:(
Maus:
Тогда описываю к нему функции add/delete и т.д..
могу ошибаться, но у товара таких функций (правильнее, методов) быть не должно. они могут быть у классов "корзина", "склад"
то это получается опять какой-то не ООПшный подход
вполне себе ООПшный. Хотя доводить дело до того, что каждая характеристика - отдельный объект..
Advanced Guest:
Долго отсутствовал. Много думал. Так и не понял. Сформулировал первый раз явно сумбурно. Попробую ещё раз.

1) Если бы все это было фиксированным. Проблемы бы не было.
Создал объект "товар", приписал ему хар-ки "объем на складе" и "цена".
Дальше создал наследованием от него объект "шуба" и объект "комп" с доп. хар-ками.

Но ведь у меня у каждого товара неопределённое количество характеристик (т.к. они задаются юзером) и опять же типов товаров может быть много (т.к. могут и воду завести). Как быть? Ведь наследование и предварительное создание классов не годится, т.к. это динамические хар-ки создаваемые юзером. Допустим одним из свойств объекта я делаю "тип товара", куда записываю как раз "шуба" или "комп" и дальше обрабатываю в функции "добавить/урезать" данные в соответствии с типом объекта. Но разве это опять же ООП_шный подход? Имхо чисто функциональный, т.к. я и в функции точно так же могу проверять тип товара и выполнять определенный кусок кода по switch-у.

Допустим я создаю объект "хар-ка", 2 типов: "текстареа" и "текст". Но как мне добавлять к объекту "товар" эти "хар-ки"? И как хранить? Неужели просто ТУПО создавать массив внутри класса товар, в который при вызове функции класса "добавить хар-ку" будет создаваться новый объект "хар-ка" и добавляться к этому классу? А разве это по ООП_шному? Нельзя ли сделать как-то более элегантно? Если тупо хранить набор "хар-к" в массиве, то это можно и на функциях сделать.

2)
Что меня еще беспокоит. Допустим я создаю функцию в классе getTovar($id) которая достает товар по ID. Которая запускается при конструкторе класса. Функция допустим из базы достает инфу о товаре. И допустим я имею набор ID товаров которые мне надо достать.
Пишу $ojb[$id]=new Tovar($id); в цикле и все достаю.
Но ведь это означает что на каждый товар будет создан объект (замусорена память?) и сделан отдельный запрос в БД (нагрузка на базу).
Плюс каждый объект будет присвоен отдельной переменной
Окей. Как же быть? Как правильно достать список товаров? Отдельная функция вроде бы не катит. Плюс хочется что бы если товар с нужным ID уже доставался из базы, то повторно не доставался. Плюс хотелось бы иметь возможность обращаться с набором объектов.
Неужели опять все сводится к неООП-шному подходу - написанию функции доставания списка товаров, статической переменной внутри класса которая будет хранить данные по уже достанным товарам и соответственно выборкой оттуда? Но ведь это опять - не ООП имхо, ведь то же я мог бы и на функциях сделать. Объясните пожалуйста.
dimagolov:
Advanced Guest, ООП не для того, чтобы делать "не как на ф-ях", а для того, чтобы разделять абстракции. И начинать строить абстракции надо не с товара (довольно низкоуровневая как для магазина), а с модели системы, причем в терминах MVC. Вот когда будете писать контроллер, то надо будет строить бизнес-логику, там пригодяться абстракции товара и ф-ии манипулирования им.
Advanced Guest:
ООП не для того, чтобы делать "не как на ф-ях",
---
dimagolov, поэтому я и спрашиваю как правильно сделать то что я хочу на ООП, а не на функциях.
dimagolov:
вообще есть мнение, что пока не чуствуете что и зачем надо выделять в абстракции, то ну его это ООП. по мере накопления опыта нужные абстракции начете видеть и использовать. без такого опыта будет ООП радм ООП и никаких преимуществ в скорости разработки/удобстве поддержки и т.п.
Advanced Guest:
dimagolov,
Спасибо за высказывание Вашего мнения.
Обращаю Ваше внимание на то, что "что и зачем надо выделять в абстракции" я понимаю.
Я спрашиваю КАК это сделать с помощью ООП и связных объектов, а не функций и массивов.
dimagolov:
Advanced Guest, не совсем понимаю Вашу проблему. Если у объекта определенных свойств может быть неопределенное кол-во, то тут есть 2 пути:
1. Создать массив данных свойств и перебирать его своими методами
2. Создать объект-контейнер для множества свойств и перебирать их или методами контейнера, или специального итератора, работающего с контейнером
Ну а про ф-ии и того проще. Методы объекта есть ф-ии. Их надо делить на публичных/защищенных/приватных. Кроме того, если есть некий фрагмент ф-ии который надо откоментировать, чтобы объяснить, что он делает, то его скорее всего надо выделить в отдельный метод. Ну и вместо copy-paste кода его надо выделять в отдельный метод.

А вот проектировать структуру классов для себя можете только Вы, так как она подразумевает понимание требований к системе
Maus:
имхо:
а с модели системы, причем в терминах MVC.
если уж оставаться в этих терминах - модель Модели и модель системы слабо связаны, пока идет проектирование.
"что и зачем надо выделять в абстракции" я понимаю.
вот это (текст всего абзаца я опустил): Допустим я создаю объект "хар-ка", 2 типов: "текстареа" и "текст"
свидетельствует об обратном.

Но ведь это означает что на каждый товар будет создан объект (замусорена память?)
оценивайте масштабы реально. Если объем данных в объекте 10 кб (5к символов в юникоде! страница текста ) - то таких объектов Вы можете создать примерно 800 на стандартных настройках (8Мб памяти).

и сделан отдельный запрос в БД (нагрузка на базу).
Плюс каждый объект будет присвоен отдельной переменной

никто не мешает сделать объект-генератор нужных объектов с 1 запроса. Я такие штуки называл коллекциями - у них были методы поиска по списку, показ списка...
Наверняка в паттернах есть что-то аналогичное и более проработанное.
Advanced Guest:
dimagolov,
за "объект итератор" спасибо. Если я правильно понял мысль в том, что вынести набор "хар-ик" в отдельный объект и работать с ним как со списком, вместо пихания обрабаботки приватного массива "прямо тут" в классе. Тогда мне так больше нравится, т.к. можно будет вынести обработку свойств и типов из объекта товар.

Maus,
никто не мешает сделать объект-генератор нужных объектов с 1 запроса.
---
Честно говоря не хочется "писать программу которая будет писать программу". Мне не кажется это хорошей идеей на данный момент. По крайней мере это не кажется мне идей которую я смогу осилить.

оценивайте масштабы реально. Если объем данных в объекте 10 кб (5к символов в юникоде! страница текста ) - то таких объектов Вы можете создать примерно 800 на стандартных настройках (8Мб памяти).
---
Да, маловато:( Вот по этому поводу ещё вопрос. А сколько реально займет допустим объект с 5 переменными, строковыми, каждая по 1кб текста в cp1251. Т.е. общий объем 5Кб... пхп (5) удвоит для хранения в уникоде или нет?
И еще побочный вопрос - сколько реально памяти уйдет на служебные вещи для обслуживания этого объекта?

свидетельствует об обратном.
---
Не могли бы Вы указать мне на мою логическую ошибку тут? Хочу понять что не так.

P.S.: Я понимаю что вопросы новичковые и наверняка глупые, но мне все-таки кажется что мне достаточно будет пары пинков в нужном направлении и дальше я раскачаюсь:)
Maus:
"писать программу которая будет писать программу"
немного неточно выразился - лучше было выбрать слово "инициализирует". То ест у Вас есть, скажем, 2 пути создания объекта - по идентификатору (и спровоцировав обращение к БД) или по готовому комплекту данных (тогда запроса не будет).
Да, маловато:(
куда Вам больше? Зараз посетителю стоит показывать десяток-другой товаров.

сколько реально памяти уйдет на служебные вещи для обслуживания этого объекта?
если честно - то без понятия. тот расчет делался приблизительно, исходя из того, что у объектов одного класса методы не хранятся раздельно (это было бы накладно). Значит, этим объемом можно пренебречь. Ну и юникод - 2 байта на символ.
е могли бы Вы указать мне на мою логическую ошибку тут? Хочу понять что не так.
логической ошибки нет, но текст, на мой взгляд, говорит о том, что Вы не имеете ясного ответа на вопрос, зачем Вам такие объекты. Для того, что может понадобиться от текстоывх типов, достаточно функций.
Advanced Guest:
текст, на мой взгляд, говорит о том, что Вы не имеете ясного ответа на вопрос, зачем Вам такие объекты
----
Maus,
В принципе тут Вы правы. И я не хочу делать ООП ради ООП. Но все вокруг талдычат что ООП это круто, а я этого НЕ вижу:(
Вот читаю сейчас книжку wrox по php, написано якобы как раз для людей которые "переходят от функций к ООП".
И имею несколько непоняток например
1) Класс работы с БД - как бы круто. Но всё что делает класс? Тупо транслирует sql_query в mysql_query если это mysql класс. В скрипте используется $db->sql_query, а $db в зависимости от типа БД создается или мускул или постгре. Казалось бы круто. Но я не понимаю для чего они там классы принимают, неужели просто не подключить файл с функциями для мускул sql_query ? Но преподносится это именно как "ООП невесть как круто", но ЧЕМ?
2) Есть пример опять же с бд, паттерн Factory
вместо switch($type) case:mysql case:postgre
используется набор классов с наследованием mysqlexceptionfactory
и преподносится это так, что в случае с функциями придется при добавлении дописывать везде обработку нового типа БД... но код с FActory в 2 раза больше и ведь точно так же надо добавлять обработку нового типа БД, только на экспешинах. Могу перепечатать код с книжки если нужно.
3) Преподносится идея "отложенного вызова", через call_user_func. Смысл типа. Есть студенты. Есть у них курсы. По умолчанию при создании класса студент выбираются только студенты. Курсы выбираются из базы только когда идет к ним обращение. Сделано это так: переменная флаг выбраны ли курсы, переменная с названием функции которая подгружает курсы, в каждой "курсовой" функции сначала вызов функции которая проверяет переменную и вызывает через call_user_func функцию которая подгружает курсы.
Идея понятная. Непонятно на кой черт городить это через call_user_func, неужели нельзя сделать то же самое, но с прямым вызовом? Тем более для каждого класса в примерах у них вызывается уникальная внутриклассовая функция.
4) Во всех примерах допустим для создания нового класса и операций с ним надо писать код. т.е. если мне нужна "гитара", я должен пойти и описать класс "гитара", дописать его использование и т.д.. Но где же тут хваленая абстракция? Ведь на функциях я могу просто принять от юзера данные по хар-ке гитары, и передав допустим createinstrument('gitar','nabor_har-ik') (где оба параметры просто переменные от юзера) автоматом получить новый инструмент. На ООП мне надо пойти и написать класс Guitar унаследовав его от инструментов. А если я пишу скрипт, в котором новые инструменыты задает юзер? Мне так 200 инструментов и описывать в коде?

Собственно из всего вышесказанного меня больше беспокоит пункт 4.

Поймите правильно. Я не против ООП и я не хочу использовать ООП ради ООП, я просто не вижу ГДЕ и в ЧЕМ это упростит и абстрагирует мне код. В книжках отлично все расписано на тему как правильно использовать ООП для разных целей. Можно расписать все по шагам и сделать. Непонятно только одно - чем это лучше.
Maus:
Вот читаю сейчас книжку wrox по php
что за книжка-то?
неужели просто не подключить файл с функциями для мускул sql_query ?
вроде бы вот подходящий пример: что Вы будете делать, если Вам в одном приложении надо 2 интерфейса к базам - одна MySQL, вторая Postgres?
Непонятно на кой черт городить это через call_user_func
Из Вашего описания - и впрямь непонятно. Я бы сделал за счёт "невозможного" значения

<?php
class Student
{
private $courses = null;

/**
* прочие свойства и методы ...
*
*/
private function load_courses()
{
// сразу переводим из невозможного состояния в допустимое
$this->courses = array();
// здесь загружаются курсы
}

public function get_courses()
{
if(is_null($this->courses))
{
$this->load_courses();
}

return $this->courses;
}
}
?>

4)...На ООП мне надо пойти и написать класс Guitar унаследовав его от инструментов.
А оно действительно надо? Если не надо, если Вас устраивает то общее, что есть у всех инструментов - плодить сущности не нужно.
Advanced Guest:
>что за книжка-то?
http://www.ozon.ru/context/detail/id/2847621/

> вроде бы вот подходящий пример: что Вы будете делать,
> если Вам в одном приложении надо 2 интерфейса к базам - одна MySQL, вторая Postgres?
Если у меня в ООП будут 2 интерфейса, то я должен будут видимо написать нечто вроде
$dbp=new Postgre();
$dbm=new Mysql();
и в дальнейшем писать
$dbp->select или $dbm->select
не сижу чем это отличается от (допустим)
select($sql,$dbp) и select($sql,$dbm)
или даже
q('select','dbp') и q('select','dbm')
То есть опять же, я не вижу зачем тут нужно ООП. Какая разница где задавать выбор базы - указанием объекта или отдельным параметром функции? При чем если в 1-ом моем контр-примере можно аргументировать возможностью в случае классов задать описание классов отдельно, не смешивая их. То во втором случае никаких проблем с этим нет!

> Из Вашего описания - и впрямь непонятно. Я бы сделал за счёт "невозможного" значения
Дело в том что мое описание достаточно точно передает суть и мне вот абсолютно понятен Ваш пример, но в книге зачем-то берется и плодиться лишняя "сущность". Предварительное задание имени функции для обратного вызова одним из методов, и последующим вызовом её через call_user_func. При этом говорится что это невесть как круто, но не объясняется ЧЕМ же это круто.


> А оно действительно надо? Если не надо, если Вас устраивает то общее,
> что есть у всех инструментов - плодить сущности не нужно.
Да похоже надо. Нельзя же быть отсталым и несовременным. Хочу так сказать владеть микроскопом, но не применять его как молоток.
Если все пишут вокруг на ООП, то логично предположить что это я идиот раз не пишу на нем?

В книгах по ООП я вижу объяснения что и как надо писать с точки зрения ООП. Но все их примеры реализуются ничуть не лучше чем на функциях. Я же хочу увидеть хотя бы ОДИН пример где ООП однозначно заруливает все остальные подходы и не является "ООП ради ООП" чего я боюсь больше всего. И где хотя бы объясняется ПОЧЕМУ оно в этом заруливает другие подходы. А не просто безаппеляционно "на ооп это надо делать так, паттерн такой-то самый крутой, пишеться так-то".
Maus:
// offtopic: Guest и впрямь Advanced (-;
Вернёмся к основам (с чего, видимо, и стоило начать). В Википедии есть хорошая статья про ООП , поэтому здесь посмотрим, в чем выигрыши на практике.
Наследование - тут всё понятно, избавляемся от копипаста. Чем плох копипаст, пояснять надо?
Полиморфизм - понятие оказалось шире, чем я думал. В примере с MySQL и Postgres для библиотеки DBSimple имеем:
$page = $db->selectPage($totalRows, 'SELECT ...', $from, $pageSize);
первый аргумент функция принимает по ссылке и записывает туда общее количество найденных строк, третий - откуда начать, четвертый - сколько выбрать.
Построение выражения LIMIT для этих СУБД различается. Но тому куску кода, в котором задана $page, это совершенно неважно. В процедурном программировании задачу реализации такого функционала обычно решают через управляющие структуры:

function selectPage (...)
{
...
switch ($dbtype)
{
case 'mysql':
$limitPart = limit_for_mysql($from, $pageSize);
break;
case 'postgres':
$limitPart = limit_for_postgres($from, $pageSize);
break;
default:
$limitPart = '';
}
...
}

при добавлении поддержки Oracle придется писать код в двух несвязанных местах - функцию limit_for_oracle() и здесь. При удалении - аналогично. А если мы "оптимизируем" управляющий код выше вот так:

$limit_func = 'limit_for_'.$dbtype;
$limitPart = $limit_func($from, $pageSize);

то одного редактора для правок может не хватить - придется использовать отладчик.
Инкапсуляция - на примере MySQL: если мы имеем одновременно 2 коннекта к различным базам, то вынуждены везде указывать идентификатор соединения.
Абстракция - можно рассмотреть шире, чем в статье: конкретный участок кода решает только свою задачу, абстрагируясь от всех косвенных деталей. То есть при выводе новостей можно написать так:
$page = $db->selectPage('SELECT FROM news ...', $from, $pageSize);
а можно абстрагироваться больше:
$page = $db->getPage($from, $pageSize);
и если структура таблицы новостей как-то изменится - этот код изменять не придется. при этом в том же проекте есть компонент статей с точно таким же методом. И при этом классы DBNews и DBArticles могут использовать общее соединение с СУБД, а могут обращаться к разным - для кода, выводящего новости, это неважно.

и последующим вызовом её через call_user_func.
цитата комментария на Озоне:
рассматривается UML, некоторые идеи экстремального программирования, разработка на основе тестирования, управление версиями и некоторые другие.
То есть вещи, которые приобретают значение при разработке больших проектов - когда есть команда, а сам проект не умещается в голове. Отсюда я делаю вывод, что в том месте авторы могли говорить о IoC (инверсии контроля). У меня нет практического опыта, который бы показал пользу этой концепции (то есть я не работал над столь большими проектами).

Если все пишут вокруг на ООП, то логично предположить что это я идиот раз не пишу на нем?
Весь мир ездит на автомобилях. Жители Тайбэя - на мотороллерах, но из это не следует, что они поголовно идиоты.
хотя бы ОДИН пример где ООП однозначно заруливает все остальные подходы
переписывание среднего или большого проекта. Если где-то в недрах проекта одна функция полагалась на то, что где-то раньше (возможно, в друром файле) другая функция подготовила какие-то данные, но вызывается эта функция только в очень редких случаях - переработка проекта будет адом.
Advanced Guest:
Maus, заходил несколько раз в тему, хотел что-то сказать, но кроме "Спасибо", ничего не придумать.
Поэтому пока просто - Спасибо. Честно.
Возражения вроде бы и есть у меня... или это вопросы, но в то же время я кажется уловил основную мысль, поэтому удалюсь подумать.
Но я ещё вернусь:)

Эта тема на forum.dklab.ru