Дисклеймер
Сразу хочу снять с себя всю ответственность за пояснения по тексту, я буду описывать отличия и особенности между паттернами в рамках своего понимания паттернов. Мне конечно пришлось проработать много литературы на этот счёт, но есть ненулевая вероятность, что моё понимание предмета и его истинное значение могут не совпадать. Поэтому не надо закидывать меня кАкушками, но за конструктивную критику в комментариях буду благодарен и готов вносить правки.
Полный перечень паттернов описан в отдельном посте.
Там же общие слова и мысли о необходимости паттернов в жизни программиста.
Фабричный метод — порождающий паттерн, задачей которого является определение единого интерфейс создания объектов в базовом классе. В общем случае дочерние классы обязаны описать собственную реализацию создающего метода.
Вообще, с рассматриваемым паттерном — фабричный метод, очень всё не просто. Далеко не любой метод создающий объекты можно назвать фабричным.
Вот например
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)
}
}
Таким образом вся логика по работе с транзакцией может быть описана в исходном абстрактном классе. А его потомки только содержат определение конкретных реализаций платёжных систем.
Но вот сейчас мне пришла мысль, а что нам может помешать, имея общий интерфейс самих платёжек прокидывать зависимость через конструктор Транзакции? Но если у порождаемых объектов нельзя выделить единый интерфейс, и вообще всё логика исходного класса транзакций меняется в зависимости от платёжки, то паттерн имеем место быть.
Итого
В чистом виде, мне с этим паттерном встречаться не приходилось. Обычно мы делали что-то очень похожее на фабричный метод, но не в его каноническом смысле.