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();
  • Вопрос задан
  • 286 просмотров
Решения вопроса 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 деньги
...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы
YCLIENTS Москва
от 200 000 до 350 000 ₽
ИТЦ Аусферр Магнитогорск
от 100 000 до 160 000 ₽
Ведисофт Екатеринбург
от 25 000 ₽
25 апр. 2024, в 20:02
100000 руб./за проект
25 апр. 2024, в 19:42
49000 руб./за проект
25 апр. 2024, в 19:41
2000 руб./за проект