R
R
ragnar_ok2021-06-27 20:18:13
PHP
ragnar_ok, 2021-06-27 20:18:13

Factory method pattern: how to get a specific product from a specific factory?

refactoring.guru defines the "Simple Factory" and "Factory Method" patterns.


The Simple Factory pattern is a class that has one method with a large conditional operator (switch - author's note) that selects the product to be created. This method is called with a parameter that specifies which product to create. A simple factory usually does not have subclasses.
class UserFactory {
    public static function create($type) {
        switch ($type) {
            case 'user': return new User();
            default:
                throw new Exception('Wrong user type passed.');
        }
    }
}


The Factory Method pattern is a class device in which subclasses can override the type of product created in the superclass.
Source: https://refactoring.guru/en/design-patterns/factor...


The following is an example of a "Factory Method" without a switch.

Thus, a "Simple Factory" is a class, and a "Factory Method" is a class device. But! Where did the switch go in the factory method? How will the client code determine which factory and which product it needs if there is no conditional statement?

Is it allowed to use a conditional statement (switch) both in the "abstract" factory class to select the desired factory, and in the concrete factory class to select the desired product? I give an example of my implementation of the "Factory Method".

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();

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
vitaly_74, 2021-06-27
@ragnar_ok

(I get the name of the desired factory and product from the request - in the controller)
For example, in yii2 there is such a thing as scripts for models, you can use the script as the name of the product.
or you can write a validator that will determine from the request which parameter to use for the factory constructor.
For example, if you have many payment systems, then the user can be given a choice of 4 links (4 payment systems) to the routers of which their names are hardcoded, i.e. pay/yandex-money or pay/webmoney
then in the client code you will have to do something like this:

... 
Validator / Scenario / Router
...
$payMethod = $validator->scenario() // yandex-money
...
$paymentSystem = new PaymentFactory($payMethod);
$paymentSystem->pay();//оплачиваем через яндекc деньги
...

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question