The Simple Factory (The Factory Pattern – part 1)

1. Definition

The Simple Factory is not a pattern discussed in the GoF book. This article is written by referring to Head First Design Patterns.

The Simple Factory pattern can be defined as a pattern where …

1. you have a factory class responsible for creating instances.

2. then, you let a client class use the factory class to get the factory’s instances,without the client knowing the instantiation logic.

2.Case study

2.1 The problem

You’re running an e-commerce website, using a credit card and PayPal for a transaction.

Here is how the controller used for transaction looks like.

class PaymentController
{
    private $payment;
    public function pay($input)
    {
        /* $input will look like this...
         *
         * $input = [
         *     'paymentMethod' => "creditcard",
         *     'totalAmount'   => 200.50,
         *     'name'          => "Malcolm Turnbull",
         *     ... and so on
         * ];
         */
        if ($input['paymentMethod'] === "creditcard") {
            $this->payment = new PayByCreditCard();
        } else if ($input['paymentMethod'] === "cash") {
            $this->payment = new PayByPayPal();
        }

        $this->payment->validateInput($input);
        $this->payment->pay($input);
        //In the real world, these 2 lines
        //would be wrapped in a try-catch loop
    }
}

How about the payment classes?

interface Payment
{
    public function validateInput($input);
    public function pay($input);
}

class PayByCreditCard implements Payment
{
    public function validateInput($input)
    {
        //This validates the customer's
        //credit card information.
        //Also, you can check if the
        //customer is black listed
        //if you have to do so.
    }

    public function pay($input)
    {
        //pay with a payment gateway.
    }
}
// PayByPayPal class has the same methods
// with different implementations

The website has been working well, but now you’re thinking about adding the following payment methods.

1. Afterpay

In case you don’t know what Afterpay is, this is a payment service started in Australia. This allows the customer to delay payment.

(I don’t want to distract you from studying… so I won’t go into details about this service here)

2. Free

You want to introduce a reward point system, where customers can buy your products by their reward points if they’ve accumulated enough points.

Let’s have a look at our PaymentController…

class PaymentController
{
    private $payment;
    public function pay($input)
    {
        $customer = Customer::find($input['customerId']);
        if ($customer->buyWithRewards()) {
            //This is what you want to introduce
            $this->payment = new FreePayment();
        } else if ($input['paymentMethod'] === "creditcard") {
            $this->payment = new PayByCreditCard();
        } else if ($input['paymentMethod'] === "paypal") {
            $this->payment = new PayByPayPal();
        } else if ($input['paymentMethod'] === "afterpay") {
            //This is another logic you want to introduce
            $this->payment = new PayByAfterpay();
        }

        $this->payment->validateInput($input);
        $this->payment->pay($input);
    }
}

As you can see, it can mess up the pay($input) function to update the logic for which payment object to instantiate.

So, what’s the problem?

The problem of directly updating this function is that it goes against the open-closed principle.

This principle has been covered in another article, but let’s recap what it was…

 classes should be open for extention, but closed for modification

As mentioned above, your e-commerce website has been working well. So, it’s ideal to keep the existing code intact as much as possible.

(Imagine that you want to abandon the reward program or you want to introduce another way of payment. You need to modify this controller each time.)

Besides, in the MVC pattern, it’s a Model’s job to hold application logic, not a Controller’s job. (I’ll discuss the MVC pattern in a future article).

So, we should consider how we can minimize the risk of breaking existing code and how it can be easier to maintain code which varies.

One way to achieve it is to encapsulate what is varying.

2.2 The solution

1. Make a class for the object creation logic

class PaymentFactory
{
    public function createPayment($customerId, $paymentMethod)
    {
        $customer = Customer::find($customerId);
        if ($customer->buyWithRewards()) {
            return new FreePayment();
        } else if ($paymentMethod === "creditcard") {
            return new PayByCreditCard();
        } else if ($paymentMethod === "paypal") {
            return new PayByPayPal();
        } else if ($paymentMethod === "afterpay") {
            return new PayByAfterpay();
        }
        //You can copy and past most of the part
    }
}

 2. Remove the object creation logic from the client

class PaymentController
{
    private $payment;
    public function pay($input)
    {
        //Gone!
        $this->payment->validateInput($input);
        $this->payment->pay($input);
    }
}

 3. Put the factory into the client.

class PaymentController
{
    private $payment;
    private $paymentFactory;

    public function __construct(PaymentFactory $paymentFactory)
    {
        $this->paymentFactory = $paymentFactory;
    }

    public function pay($input)
    {
        $this->payment = $this->paymentFactory->createPayment($input['customerId'], $input['paymentMethod']);
        //Welcome back!

        $this->payment->validateInput($input);
        $this->payment->pay($input);
    }
}

2.3 The outcome

As a result, you’ve got only one place to make changes on which payment method to use.

Also, if you ever need to create another transaction page with a new controller, the PaymentFactory can be used there.

3. The structure

NOTE: The factory can use a static method for object creation, but remember that you can’t subclass and modify the object creation method.

4. When to use this pattern

This becomes handy when you need to instantiate different classes with if statements or switch statements. As mentioned in the case study, this can be particularly useful in controllers.

Also, this is helpful when you have to separate the responsibility of creating instances from another class, which is, in the case study, PaymentController.

Leave a Reply

Your email address will not be published. Required fields are marked *