Что такое фабрики в PHP

  • Что такое фабрики в PHP

    Антон Долганин 8 Июня 2015 9:00 3984
    Нет, тут не будет подробного объяснения что это за паттерны и с чем их едят, и описаний  десятков ситуаций, которые, быть может, вообще вам никогда не пригодятся. Я вообще противник всего искусственного и выступаю за обучение на практике. Просто в помощь почаще надо призывать элементарную логику, и все получится.

    Простая задача, с которой я столкнулся. Есть набор однотипных классов с одинаковыми методами. Причем подразумевается постоянное расширение этого множества. Так как я обещал практику, пусть это будет игровой бот в игре, который умеет:
    • рождаться
    • нападать
    • умирать
    Почему мы не можем все затолкать в один класс? Ну как минимум, мы даем возможность расширения игры ботами, а также подразумеваем, что у каждого бота своя уникальная природа поведения. Всего три метода, но они всегда разные.

    Так как все три метода должны быть строго обязательны у каждого бота, я обязал их быть через интерфейс:
    interface iBot
    {
       public function born();
       public function attack();
       public function destruct();
    }


    Зачем я допустил такую заумность? Просто чтобы расширяющему ботов разработчику PHP сразу выдал исключение, если он запустит своего бота в систему. Это убережет от ошибок, не видных на первый взгляд.

    Программируем бота:
    class Test implements iBot
    {
       public function born()
       {
       }
    
       public function attack()
       {
       }
    
       public function destruct()
       {
       }
    }


    Ну а теперь нам осталось сделать общий некий класс, чтобы делать вот так:
    $a = new Bot('Test');//передаем имя бота, совпадающее с именем его класса
    $a->born();
    $a->attack();
    $a->destruct();


    В начале я упомянул логику. Что подсказывает логика? Логика подсказывает сделать вот так:
    class Bot
    {
       public function __construct($id)
       {
          return new $id;
       }
    }


    То есть мы хотим создать новый класс внутри класса и вернуть его, чтобы дальше (в Игре) нам не задумываться о том, каким ботом мы орудуем (и  их может быть много). И радостно ждем результата. Неа, результата не будет, потому что ну вот так вот. Нельзя так делать.

    Что подсказывает дальше логика? Избавиться от внешнего new, и переделать  класс Bot примерно вот так:
    class Bot
    {
       public function create($id)
       {
          return new $id;
       }
    }


    Тогда вызов у нас стал полностью "абстрактен":
    $a = Bot::create('test');
    $a->born();
    $a->attack();
    $a->destruct();


    И тут появляется вопрос, почему сразу нельзя сделать new Test? Тут данная логика опущена, но:
    • бота может не существовать, или он уже выведен из игры, система должна адекватно отреагировать на уровне create
    • бот может быть не доступен на данной локации, аналогично должна игра отреагировать
    • в конце концов, мы можем запустить создание в цикле, чтобы заполнить ботами локацию, и нам до фени какие боты будут
    После этого я узнал, что данный подход называется "фабрика" или "factory". Мне это почему-то не пригождалось целых 10 лет, и я рад, что я не забивал голову этой ерундой. Осталась чиста логика :)

    PS: А собеседующих при приеме на работу разными умными паттернами я вам предлагаю послать нафик и дать почитать этот пример из жизни.
Виталий
8 Июня 2015 9:42
у нас часто при приеме на работу всегда пытаются доказать что ты тупень и не знаешь элементарных вещей, а на деле выясняется что тот кто проводил собеседование вычитал много умных букв, а сам толком ничего не представляет....

а по концовке согласен, в большинстве случаем данные методы редко попадаются к использованию.....
Женя
8 Июня 2015 21:49
Для данной задачи я бы использовал паттерн конвеер, так как события связанные с рождением нападением и смертью - последовательны, чтобы вызов одного метода не был до вызоыва друогож на всякий случай.
Антон Долганин
9 Июня 2015 6:39
Спасибо за уточнение.
Женя
9 Июня 2015 7:58
Антон, если Вас не затруднит добавьте хоть и малознакомого коллегу (меня) в други на битриксе, а то под вашими поставми я писать не могу:)
Никита
9 Июня 2015 8:56
Плохо:
$a = Bot::create('Test'); 
Хорошо:
$a = Bot::create(new Test); 
В этом случае сработает автозаполнение ИДЕ и будет обеспечен удобный рефакторинг.