# 三种工厂模式对比

> 例子来自<大话设计模式>一书，php代码由个人实现

**简单工厂模式：**

* 一个抽象产品类（可以是：接口，抽象类，普通类），可以派生出多个具体产品类。&#x20;
* 单独一个具体的工厂类。
* 每个具体工厂类只能创建一个具体产品类的实例。&#x20;

**工厂方法模式：**

* 一个抽象产品类（可以是：接口，抽象类，普通类），可以派生出多个具体产品类。&#x20;
* 一个抽象工厂类（可以是：接口，抽象类），可以派生出多个具体工厂类。&#x20;
* 每个具体工厂类只能创建一个具体产品类的实例。&#x20;

**抽象工厂模式：**

* 多个抽象产品类（可以是：接口，抽象类，普通类），每个抽象产品类可以派生出多个具体产品类。&#x20;
* 一个抽象工厂类（可以是：接口，抽象类），可以派生出多个具体工厂类。&#x20;
* 每个具体工厂类可以创建多个具体产品类的实例。&#x20;

**区别：**

* 简单工厂模式只有一个抽象产品类，只有一个具体的工厂类。
* 工厂方法模式只有一个抽象产品类，而抽象工厂模式有多个抽象产品类。
* 工厂方法模式的具体工厂类只能创建一个具体产品类的实例，而抽象工厂模式可以创建多个具体产品类的实例。

## **简单工厂模式**

**UML类图如下：**

![](https://2166326059-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30n_07ZN5S7w5Tb%2F-LfnT5w3Liyn75DIsY2n%2F-LfnT6oIO7pjD4-r1pnD%2Ffactory_duibi_simple.png?generation=1558862934080393\&alt=media)

当需要加法类的时候，调用工厂类的CreateOperate()，要指定制造的Product

调用工厂，需要createOperator("/")，就能返回除法运算符。

* 优点：客户端不需要修改代码。
* 缺点： 当需要增加新的运算类的时候，不仅需新加运算类，还要修改工厂类，违反了开闭原则。

**简单工厂模式程序设计图：**

![](https://2166326059-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30n_07ZN5S7w5Tb%2F-LfnT5w3Liyn75DIsY2n%2F-LfnT6oK-9w2Uaf2J-7i%2Ffactory_duibi_%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E5%9B%BE.jpg?generation=1558862934479527\&alt=media)

**具体实现：**

1）抽象产品（Product）角色：运算抽象类(Operation)。

```php
// 运算抽象类
class Operation{

    // 数字A
    protected $_numberA = null;

    // 数字B
    protected $_numberB = null;

    /**
     * 设置成员A
     *
     * @param double $num 数字
     * @return void
     */
    public function setNumberA($num){
        $this->_numberA = $num;
    }

    /**
     * 获取成员A
     *
     * @return double 数字
     */
    public function getNumberA(){
        return $this->_numberA;
    }

    /**
     * 设置成员B
     *
     * @param double $num 数字
     * @return void
     */
    public function setNumberB($num){
        $this->_numberB = $num;
    }

    /**
     * 获取成员B
     *
     * @return double 数字
     */
    public function getNumberB(){
        return $this->_numberA;
    }

    /**
     * 获取运算结果
     *
     * @return double 数字
     */
    public function getResult(){
        return null;
    }
}
```

2）具体产品（Concrete Product）角色：加法运算(OperationAdd)，减法运算(OperationSub)，乘法运算(OperationMul)，除法运算(OperationDiv)。

```php
// 加法类
class OperationAdd extends Operation{

    /**
     * 获取运算结果
     *
     * @return double 数字
     */
    public function getResult(){
        return $this->_numberA + $this->_numberB;
    }
}

// 减法类
class OperationSub extends Operation{

    /**
     * 获取运算结果
     *
     * @return double 数字
     */
    public function getResult(){
        return $this->_numberA - $this->_numberB;
    }
}

// 乘法类
class OperationMul extends Operation{

    /**
     * 获取运算结果
     *
     * @return double 数字
     */
    public function getResult(){
        return $this->_numberA * $this->_numberB;
    }
}

// 除法类
class OperationDiv extends Operation{

    /**
     * 获取运算结果
     *
     * @return double 数字
     */
    public function getResult(){
        if ($this->_numberB == 0) {
            return null;
        }
        return $this->_numberA / $this->_numberB;
    }
}
```

3）工厂（Creator）角色：工厂类(OperationFactory)。

```php
/**
 *
 * 设计模式：简单工厂模式
 * 
 * 模式简介：用一个单独的类来创造实例化的过程，叫做简单工厂。好处将来增加或减少
 * 实例只需要修改工厂即可。
 *
 */


// 创建一个工程，用来生产实例
class OperationFactory{

    /**
     * 根据运算不同实例不同的对象
     *
     * @return object 返回实例化的对象
     */
    public static function createOperate($operate){
        $oper = null;
        switch ($operate) {

            // 实例加法类
            case '+' :
                $oper = new OperationAdd();
                break;

            // 实例减法类
            case '-' :
                $oper = new OperationSub();
                break;

            // 实例乘法类
            case '*' :
                $oper = new OperationMul();
                break;

            // 实例乘法类
            case '/' :
                $oper = new OperationDiv();
                break;

            default :
                $oper = null;
        }

        return $oper;
    }
}

/*
* 客户端
*
*/
// 工厂创建实例
$operObject = OperationFactory::createOperate('+');

if ($operObject == null) {
    return '$operate not found';
}

// 设置数字A
$operObject->setNumberA(5);

// 设置数字B
$operObject->setNumberB(2);

// 运算
echo $operObject->getResult() . "\n";
```

## **工厂方法模式**

UML类图如下：

![](https://2166326059-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30n_07ZN5S7w5Tb%2F-LfnT5w3Liyn75DIsY2n%2F-LfnT6oNvj_xO8-zZsVd%2Ffactory_duibi_method.png?generation=1558862933683635\&alt=media)

这个和简单工厂有区别，简单工厂模式只有一个工厂，工厂方法模式对每一个产品都有相应的工厂

* 好处：增加一个运算类（例如N次方类），只需要增加运算类和相对应的工厂，两个类，不需要修改工厂类。
* 缺点：增加运算类，会修改客户端代码，工厂方法只是把简单工厂的内部逻辑判断移到了客户端进行。

改造简单工厂代码中的`工厂（Creator）角色`,代码如下：

```php
interface IFactory
{
    public function CreateOperation();
}

class AddFactory implements IFactory
{
    public function CreateOperation()
    {
        return new OperationAdd();
    }
}

class SubFactory implements IFactory
{
    public function CreateOperation()
    {
        return new OperationSub();
    }
}

class MulFactory implements IFactory
{
    public function CreateOperation()
    {
        return new OperationMul();
    }
}

class DivFactory implements IFactory
{
    public function CreateOperation()
    {
        return new OperationDiv();
    }
}

/*
 * 客户端代码
 * 
 * */

// 工厂创建实例
$operationFactory = new AddFactory();
$operation = $operationFactory->CreateOperation();
// 设置数字A
$operation->setNumberA(10);
// 设置数字B
$operation->setNumberB(10);
// 运算
echo $operation->getResult() . "\n";
```

**抽象工厂模式：**

UML类图如下：\
![](https://2166326059-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT30n_07ZN5S7w5Tb%2F-LfnT5w3Liyn75DIsY2n%2F-LfnT6oPrv4csQkG2TPO%2Ffactory_duibi_abstract.png?generation=1558862934229032\&alt=media)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaoxiami.gitbook.io/php/chapter1/gong-chang-mo-shi/san-zhong-gong-chang-mo-shi-dui-bi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
