PHP Design Pattern Series - Factory Method Pattern (Factory Method) (transfer)

1. Mode Definition

Define an interface for creating objects, but let subclasses instantiate concrete classes. The Factory Method pattern defers instantiation of a class to subclasses.

2, the problem leads

The framework needs to provide a standardized architectural model for multiple applications, while also allowing independent applications to define and instantiate their own domain objects.

3. Solutions

Factory methods solve the above problems by creating objects in the form of template methods. The parent class defines all standard common behavior, and then implements the creation details into the child class and outputs it to the client.

People often use the factory pattern as the standard way to create objects, but the factory method is not necessary in these situations: the instantiated class never changes; or the instantiation occurs in an operation that a subclass can easily override (like initialization).

4, UML class diagram

Factory-Method-UML

5. Sample code

FactoryMethod.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 * Factory method abstract class
 */
abstract class FactoryMethod
{

    const CHEAP = 1;
    const FAST = 2;

    /**
     * Subclasses must implement this method
     *
     * @param string $type a generic type
     *
     * @return VehicleInterface a new vehicle
     */
    abstract protected function createVehicle($type);

    /**
     * Create new vehicle
     *
     * @param int $type
     *
     * @return VehicleInterface a new vehicle
     */
    public function create($type)
    {
        $obj = $this->createVehicle($type);
        $obj->setColor("#f00");

        return $obj;
    }
}

ItalianFactory.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 *ItalianFactory is a car factory in Italy
 */
class ItalianFactory extends FactoryMethod
{
    /**
     * {@inheritdoc}
     */
    protected function createVehicle($type)
    {
        switch ($type) {
            case parent::CHEAP:
                return new Bicycle();
                break;
            case parent::FAST:
                return new Ferrari();
                break;
            default:
                throw new \InvalidArgumentException("$type is not a valid vehicle");
        }
    }
}

GermanFactory.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 * GermanFactory is a German car factory
 */
class GermanFactory extends FactoryMethod
{
    /**
     * {@inheritdoc}
     */
    protected function createVehicle($type)
    {
        switch ($type) {
            case parent::CHEAP:
                return new Bicycle();
                break;
            case parent::FAST:
                $obj = new Porsche();
                //Because we already know what object it is, we can call specific methods
                $obj->addTuningAMG();

                return $obj;
                break;
            default:
                throw new \InvalidArgumentException("$type is not a valid vehicle");
        }
    }
}

VehicleInterface.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 * VehicleInterface is the vehicle interface
 */
interface VehicleInterface
{
    /**
     * Set the color of the car
     *
     * @param string $rgb
     */
    public function setColor($rgb);
}

Porsche.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 * Porsche
 */
class Porsche implements VehicleInterface
{
    /**
     * @var string
     */
    protected $color;

    /**
     * @param string $rgb
     */
    public function setColor($rgb)
    {
        $this->color = $rgb;
    }

    /**
     * Although only Mercedes-Benz cars have the AMG brand, here we provide an empty method for code example only
     */
    public function addTuningAMG()
    {
    }
}

Bicycle.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 * Bicycle
 */
class Bicycle implements VehicleInterface
{
    /**
     * @var string
     */
    protected $color;

    /**
     * Set the color of the bike
     *
     * @param string $rgb
     */
    public function setColor($rgb)
    {
        $this->color = $rgb;
    }
}

Ferrari.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

/**
 *Ferrari
 */
class Ferrari implements VehicleInterface
{
    /**
     * @var string
     */
    protected $color;

    /**
     * @param string $rgb
     */
    public function setColor($rgb)
    {
        $this->color = $rgb;
    }
}

6. Test code

Tests/FactoryMethodTest.php

<?php

namespace DesignPatterns\Creational\FactoryMethod\Tests;

use DesignPatterns\Creational\FactoryMethod\FactoryMethod;
use DesignPatterns\Creational\FactoryMethod\GermanFactory;
use DesignPatterns\Creational\FactoryMethod\ItalianFactory;

/**
 * FactoryMethodTest is used to test the factory method pattern
 */
class FactoryMethodTest extends \PHPUnit_Framework_TestCase
{

    protected $type = array(
        FactoryMethod::CHEAP,
        FactoryMethod::FAST
    );

    public function getShop()
    {
        return array(
            array(new GermanFactory()),
            array(new ItalianFactory())
        );
    }

    /**
     * @dataProvider getShop
     */
    public function testCreation(FactoryMethod $shop)
    {
        // This method plays the role of the client, we don't care what factory, we only know that we can use it to build cars
        foreach ($this->type as $oneType) {
            $vehicle = $shop->create($oneType);
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\VehicleInterface', $vehicle);
        }
    }

    /**
     * @dataProvider getShop
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage spaceship is not a valid vehicle
     */
    public function testUnknownType(FactoryMethod $shop)
    {
        $shop->create('spaceship');
    }
}

7. Summary

The factory method pattern is somewhat similar to the abstract factory pattern, but there are differences.

The factory method provides a factory class for each product, and creates different product instances through different factory instances. In the same hierarchical structure, any product can be added.

The abstract factory is to deal with the concept of product family. For example, each automobile company may produce cars, trucks, and passenger cars at the same time, so each factory must have a method to create cars, trucks, and passenger cars. In response to the concept of product family, it is easy to add new product lines, but it is impossible to add new products.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324888578&siteId=291194637