ragnar_ok
@ragnar_ok

Паттерн "Фабричный метод": как получить конкретный продукт из конкретной фабрики?

refactoring.guru дает определение паттернам "Простая фабрика" и "Фабричный метод".

Паттерн Простая фабрика — это класс, в котором есть один метод с большим условным оператором (switch — прим. автора), выбирающим создаваемый продукт. Этот метод вызывают с неким параметром, по которому определяется какой из продуктов нужно создать. У простой фабрики, обычно, нет подклассов.
class UserFactory {
    public static function create($type) {
        switch ($type) {
            case 'user': return new User();
            default:
                throw new Exception('Wrong user type passed.');
        }
    }
}


Паттерн Фабричный метод — это устройство классов, при котором подклассы могут переопределять тип создаваемого в суперклассе продукта.
Источник: https://refactoring.guru/ru/design-patterns/factor...


Далее приводится пример "Фабричного метода" без switch.

Таким образом, "Простая фабрика" — это класс, а "Фабричный метод" — устройство классов. Но! Куда пропал switch в фабричном методе? Каким образом клиентский код будет определять какая именно фабрика и какой именно продукт ему нужен, если отсутствует условный оператор?

Допускается ли использовать условный оператор (switch) как в классе "абстрактной" фабрики, чтобы выбрать нужную фабрику, так и в классе конкретной фабрики, чтобы выбрать нужный продукт? Привожу пример моей реализации "Фабричного метода".

interface ProductFactoryInterface
{
    public static function create(string $product): ProductInterface;
}
 
class ProductFactory
{
    public static function create(string $factory, string $product): ProductInterface
    {
        switch ($factory) {
            case 'acme':
                return AcmeFactory::create($product);
 
            default:
                throw new InvalidArgumentException();
        }
    }
}
 
class AcmeFactory implements ProductFactoryInterface
{
    public static function create(string $product): ProductInterface
    {
        switch ($product) {
            case 'foo':
                return new FooProduct();
 
            case 'bar':
                return new BarProduct();
 
            default:
                throw new InvalidArgumentException();
        }
    }
}
 
interface ProductInterface
{
    public function getName(): string;
}

class FooProduct implements ProductInterface
{
    public function getName(): string
    {
        return 'foo';
    }
}
 
class BarProduct implements ProductInterface
{
    public function getName(): string
    {
        return 'bar';
    }
}

// клиентский код

$product = ProductFactory::create('acme', 'foo');
$product->getName();
  • Вопрос задан
  • 282 просмотра
Решения вопроса 1
vitaly_74
@vitaly_74
(получаю название нужной фабрики и продукта из запроса — в контроллере)
Например в yii2 есть такая штука как сценарии у моделей, вы можете использовать сценарий как имя продукта.
либо вы можете написать валидатор, который из запроса будет определять, какой параметр использовать для конструктора фабрики.
Например, если у вас много платежных систем, то пользователю можно дать на выбор 4 ссылки (4 платежные системы) в роутеры которых зашиты их названия, т.е. pay/yandex-money или pay/webmoney
тогда в клиентском коде вы должны будете что то типо такого сделать:
... 
Validator / Scenario / Router
...
$payMethod = $validator->scenario() // yandex-money
...
$paymentSystem = new PaymentFactory($payMethod);
$paymentSystem->pay();//оплачиваем через яндекc деньги
...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы