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


Advanced Guest: Подскажите как правильно сделать на классах/ооп
Всё-таки я немного застрял с ооп и не могу найти элегантного выхода.
Бакграунд: есть наборы типа лего, разных деталей около 30, могут добавляться легко. каждый набор составляется и после чего их закупают.
Интерфейс нечто вроде того: анлимитед полей (добавляются на яваскрипт), выбирается селектом тип детали, пишется количество, после чего жмем сэйв и все попадает в базу.
Каждое поле там по ходу может по разному обрабатываться, т.е. для каждого функции validate/prepare2bd/checkrights и т.д. - все разные.
Было сделано на функциях.
Проходит форич по всем полям по очереди, берется тип в переменную $type, после чего делается include("$type/validate.php"); или include("$type/prepare2bd.php");.
Функции аккуратно лежали по файлам, минимум кода для их подключения ($type естественно проверяется сначала на наличие файла в этой директории).
Но идея - как бы объекты, функции, вроде ооп-шная. Т.е. есть объекты из них составляется больший объект, наборы методов у всех разные.
Что сделал - интерфейсик (на фиг он нужен я честно говоря не понял, но идеология вроде требует, т.к. набор функций везде разный). Куча классов class validate class prepare2bd и т.д.
Не идеально, но вроде бы ничего так.
Есть класс Nabor в котором есть приватный массив в котором хранятся эти самые объекты деталей (это кстати правильно или можно красивее? все-таки набор объектов... неужели нужно просто тупо в массивах хранить? ).
Но застрял я вот на чем.
Если раньше я делал include("$type/validate.php"); и все, то теперь это разрастается в жуткую конструкцию вида
if($type=='square') $nabor->add(new Square(value));
elseif($type=='rectangle') $nabor->add(new Rectangle($value));
мало того что меня раздражают эти 30 ифов, так еще теперь недостаточно просто добавить файлы обработчика что бы появился обработчик. Надо менять основной код с ифами.
Уверен на 100% что я что-то делаю не так, т.к. елки палки, ведь ООП предназначено что бы вносить изменения и добавления (ну по идее) не трогая основной код и все такое.
Конструкции вида eval($nabor->add(new $key)) - считаю костылями, которые применять грешновато. Я не прав?
В общем вопрос - какой нормальный выход в моей ситуации?
Maus:
Advanced Guest
кажется, так:

$validator = ValidatorFactory::get($type, $value);
$nabor->add($validator);

(промежуточная переменная - чисто для наглядности).
Посмотрите паттерн Фабрика (или здесь какой другой паттерн лучше подойдет?).
Advanced Guest:
Если я правильно понимаю смысл "фабрики классов", то это будет именно те же самые "30 ифов", только внутри класса.
Т.е. в данном случае Вы предлагаете, если я Вас правильно понял, сделать нечто вроде

class ValidatorFactory {
static function get ($type,$value) {
if($type=='square') return new Square;
elseif($type=='circle') return new Circle
}
}

Да, я запихну эти свитчи в отдельный класс, но мою проблему это не решит.
Мне всё так же придется дописывать основной код, когда я добавляю очередную "детальку".
Об этом кстати вот здесь же сказано в "недостатках": http://www.citforum.ru/SE/project/pattern/p_2.shtml#3.3.1
Недостатки Интерфейс "Абстрактной фабрики" фиксирует набор обьектов, которые можно создать. Расширение "Абстрактной фабрики" для изготовления новых обьектов часто затруднительно.
Advanced Guest:
То есть моя цель сделать именно такого же плана автомат как я имел на функциях include("$type/add.php"); и все.
В том случае основной рабочий код необходимости трогать не было и его можно было даже зендануть, а все новые "детальки" просто ложились в соответствующую директорию. Вот я хочу такого же решения, только на классах и идеологически верного.
P.S.: Надо бы наверное зарегиться, вроде уже пора, а пока отдельное спасибо что отвечаете анониму:)
Advanced Guest:
Да, вот еще. Нашел в одной из книжек.
Там паттерн Фактори как раз применяется для того, что я уже сделал.
Т.е. что бы не было
switch($connecttype)
case mysql: mysql_connect
case mssql: mssql_connect
и основным его преимуществом называют что все методы работы с mysql выносятся в отдельный класс, а потом вызывается new Mysql и все, и все связанное с mysql в отдельном файле. Но у меня все методы работы с "детальками" разными уже разложены по отдельным классам и проблема стоит на другом уровне, как в начале скрипта избавиться от кучи ифов вида if(mysql)new Mysql elseif(pgsql) new Pgsql.
Maus:
Advanced Guest
можно кучу if-ов заменить на менее безопасный вариант - class_exists() . Что тут можно еще придумать, я не знаю
Advanced Guest:
Maus,
Я ведь не столько о проверке существования класса, сколько о создании правильного экземпляра класса на основе переменной без if-ов.
В общем пришлось использовать некрасивый вариант
$var=new $type();
$type->validate();
Если ничего более красивого нет, то на нем и остановлюсь.

Единственный вопрос. Вышеупомянутый вид записи в отношении классов - он легален и легален на всех версиях php?
Rumata:
Advanced Guest
сдается мне, что вам нужен статический метод-"фабрика" кажется так он называется

class Core {
static function getInstance($name)
{
if ( ( @include_once "${name}.php" ) && class_exists($name) ) {
return new $name;
} else {
throw new Exception("Unknown class $name.");
}
}
}

...
$var = Core::getInstance($name);
$varList->add($var);

Advanced Guest:
Rumata,
А для чего именно мне нужен статический метод фабрика? Причина?
includeonce и class_exist отлично заменяются __autoload, а new $name я и прямо в коде могу написать.
То есть, мне действительно интересно зачем здесь делать отдельный класс для этого.
Если бы я мог избавиться от new $name - вот это было бы круто, но ведь не избавляюсь.
WingedFox:
Advanced Guest
Фабрика хороша тем, что это единая точка создания объектов, что хорошо, например, для ведения логов.

Вместо new $name лучше использовать $name::getInstance(), который будет знать как именно создавать самого себя.
Кроме того, имея фабрику позже можно реализовать определённый API для автоматического создания полнофункциональных объектов, например при восстановления состояния из сессии.

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