Дисклеймер
Сразу хочу снять с себя всю ответственность за пояснения по тексту, я буду описывать отличия и особенности между паттернами в рамках своего понимания паттернов. Мне конечно пришлось проработать много литературы на этот счёт, но есть ненулевая вероятность, что моё понимание предмета и его истинное значение могут не совпадать. Поэтому не надо закидывать меня кАкушками, но за конструктивную критику в комментариях буду благодарен и готов вносить правки.
Полный перечень паттернов описан в отдельном посте.
Там же общие слова и мысли о необходимости паттернов в жизни программиста.
Фабричный метод — порождающий паттерн, задачей которого является определение единого интерфейс создания объектов в базовом классе. В общем случае дочерние классы обязаны описать собственную реализацию создающего метода.
Вообще, с рассматриваемым паттерном — фабричный метод, очень всё не просто. Далеко не любой метод создающий объекты можно назвать фабричным.
Вот например
class Time { private $hour; private $minute; private $second; public function __construct(int $hour, int $minute, int $second) { $this->hour = $hour; $this->minute = $minute; $this->second = $second; } public static function fromString(string $time) { if (!preg_match('/\d+:\d+:\d+/', $time)) { throw new DomainException('Fail string format. Must by H:i:s'); } $explode = explode(':', $time); return new self($explode[0], $explode[1], $explode[2]); } }
Метод fromString не может носить гордое название паттерна «Фабричный метод». Поскольку «Фабричный метод» предполагает иерархию объектов. Т.е. должен быть некий родительский класс с порождающим методом, возможно даже абстрактным порождающим методом. А все дочерние классы, должны определять свою реализацию этого метода. И вот тогда мы имеем дело с реализацией рассматриваемого паттерна.
Правильный пример
Есть объект транзакций, который должен уметь работать с несколькими платёжными системами.
abstract class Transaction { abstract public function getBilling(): BillingService; public function setPayment(int $hour, int $minute, int $second) { $billingService = $this->getBilling(); // создание транзакции в БД $id = $this->createInBd(); // отправка запроса в платежный сервис $res = $billingService->sendPayments(); // при успешности установим локальный статус успешности оплаты if( $res ) { $this->setSuccess(); } } }
Метод setPayment внутри себя использует BillingService, который инстанцируется специальным методом, тем самым «Фабричным методом».
Теперь предположим, что у нас есть 2 платёжных сервиса: Billing1 и Billing2. Они работают внутри себя по-разному, но должны реализовывать единый интерфейс, например
interface BillingService { public function sendPayments(); public function getStatus(); }
Не будем влезать в конкретику реализации. Предположим, что первая платёжка в конструктор принимает 1 параметр — токен, а вторая 2 параметра, имя аккаунта и токен. А теперь пропишем как бы могли выглядеть реализации класса транзакции для этих платёжек.
class Billing1Transaction extends Transaction { private $token; public function __construct(sting $token) { $this->token = $token; } public function getBilling(): BillingService { return new Billing1($this->token) } } class Billing2Transaction extends Transaction { private $account; private $token; public function __construct(sting $account, sting $token) { $this->account = $account; $this->token = $token; } public function getBilling(): BillingService { return new Billing2($this->account, $this->token) } }
Таким образом вся логика по работе с транзакцией может быть описана в исходном абстрактном классе. А его потомки только содержат определение конкретных реализаций платёжных систем.
Но вот сейчас мне пришла мысль, а что нам может помешать, имея общий интерфейс самих платёжек прокидывать зависимость через конструктор Транзакции? Но если у порождаемых объектов нельзя выделить единый интерфейс, и вообще всё логика исходного класса транзакций меняется в зависимости от платёжки, то паттерн имеем место быть.
Итого
В чистом виде, мне с этим паттерном встречаться не приходилось. Обычно мы делали что-то очень похожее на фабричный метод, но не в его каноническом смысле.