php设计模式(三)简单工厂模式

本来这篇文章早就写完了;
简单工厂模式
最先准备用 IOS 和 Android 举例;
但是后来考虑到工厂方法和抽象工厂模式;
又用小米和华为重写了一遍;
这样可以方便的扩充低配版的红米和荣耀;
但还是觉得讲的不清楚;
最后采用了程杰老师《大话设计模式》书中的加减乘除举例;
把 C# 的代码转化并加上自己的理解写成 PHP 代码;
在此感谢程杰老师;
反复写了3种示例终于把这篇文章定稿了;

简单工厂并不属于23种设计模式;
它的实现和它的名字气质很符;
就是简单;
但是它使用的比较多;
当面对产品经理那句让人肝儿颤的"此处不要写死以后可能会改"的时候;
以及"以后可能要增加**功能"的时候;
那我们都可以先构思下是否适合简单工厂模式;
先来说说应用场景;

应用场景

在不确定有多少种操作的时候;
比如说运算符号 + - * / ;

结构

1个工厂;
1个 interface 或者 abstract 产品父类;
多个实现 interface 或者继承 abstract 的具体产品类;

示例

写一个计算器;
我们先用直观的但是并不优雅的方式来实现;
Bad.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 不好的示例
 *
 * Class Bad
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Bad
{
    /**
     * 计算结果
     *
     * @param int $numberA
     * @param string $operate
     * @param int $numberB
     * @return int|float|int
     */
    public function getResult($numberA, $operate, $numberB)
    {
        switch ($operate) {
            case '+':
                $result = $numberA + $numberB;
                break;
            case '-':
                $result = $numberA - $numberB;
                break;
            case '*':
                $result = $numberA * $numberB;
                break;
            case '/':
                if ($numberB != 0) {
                    $result = $numberA / $numberB;
                } else {
                    throw new \InvalidArgumentException('除数不能为0');
                }
                break;
            default:
                throw new \InvalidArgumentException('暂不支持的运算');
        }
        return $result;
    }
}

这个类很简单就是传2个值和一个运算符号计算结果;

        $program = new Bad();
        $result = $program->getResult(1, '+', 2);
        echo $result;

但是这种方式很不OOP;
把逻辑判断流程和实现代码各种混杂在一起不利于扩展;
就比如说除法的 case 里面的判断;
这时候我们可以把各种运算独立封装;
先建立一个 abstract ;
Operation.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 操作类型抽象类
 *
 * Class Operation
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
abstract class Operation
{
    /**
     * 运算符号左边的值
     *
     * @var int
     */
    protected $numberA = 0;

    /**
     * 运算符号右边的值
     *
     * @var int
     */
    protected $numberB = 0;

    /**
     * 计算结果
     *
     * @return mixed
     */
    abstract public function getResult();

    /**
     * 给 numberA 赋值
     *
     * @param $number
     */
    public function setNumberA($number)
    {
        $this->numberA = $number;
    }

    /**
     * 给 numberB 赋值
     *
     * @param $number
     */
    public function setNumberB($number)
    {
        $this->numberB = $number;
    }

}

然后是四则运算;
Add.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 加法
 *
 * Class Add
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Add extends Operation
{
    /**
     * 计算结果
     *
     * @return int
     */
    public function getResult()
    {
        return $this->numberA + $this->numberB;
    }
}

Sub.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 减法
 *
 * Class Sub
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Sub extends Operation
{
    /**
     * 计算结果
     *
     * @return int|mixed
     */
    public function getResult()
    {
        return $this->numberA - $this->numberB;
    }
}

Mul.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 乘法
 *
 * Class Mul
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Mul extends Operation
{
    /**
     * 计算结果
     *
     * @return float|int
     */
    public function getResult()
    {
        return $this->numberA * $this->numberB;
    }
}

Div.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 除法
 *
 * Class Div
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Div extends Operation
{
    /**
     * 计算结果
     *
     * @return float|int
     */
    public function getResult()
    {
        if ($this->numberB == 0) {
            throw new \InvalidArgumentException('除数不能为0');
        }
        return $this->numberA / $this->numberB;
    }
}

这时候我们就可以使用单独的类计算了;

        // 计算 1+2
        $operation = new Add();
        $operation->setNumberA(1);
        $operation->setNumberB(2);
        $result = $operation->getResult();
        echo $result;

        echo '<br>';

        // 计算 3+4
        $operation = new Add();
        $operation->setNumberA(3);
        $operation->setNumberB(4);
        $result = $operation->getResult();
        echo $result;

当我们在代码中大量的 new 了加减乘除;
如果说我们需要要把 Add 改成 Addition ;
或者说 new 的时候需要传个参数;
这就坑爹了;
全场改起来想想都心累;
于是被这种场景坑惨了的前辈们就总结出了简单工厂模式;
张大眼睛有请我们的主角 Factory 上场;
Factory.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

/**
 * 工厂类
 *
 * Class Factory
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Factory
{
    /**
     * 创建一种运算
     *
     * @param $operate
     * @return Add|Div|Mul|Sub
     */
    public function create($operate)
    {
        switch ($operate) {
            case '+':
                $result = new Add();
                break;
            case '-':
                $result = new Sub();
                break;
            case '*':
                $result = new Mul();
                break;
            case '/':
                $result = new Div();
                break;
            default:
                throw new \InvalidArgumentException('暂不支持的运算');
        }
        return $result;
    }
}

当我们把这都拆开了来看的时候;
就会发现简单这个名字真是没白起;
它就是把 Bad.php 中的 case 换成具体的类;

        $factory = new Factory();
        $operation = $factory->create('+');
        $operation->setNumberA(1);
        $operation->setNumberB(2);
        $result = $operation->getResult();
        echo $result;

因为我们全场都是 new 的 Factory ;
并没有 new 具体的 Add 类;
当我们面临把 Add 改成 Addition 的时候;
我们只需要在 Factory 中改一处 new Add() 就行了;

运行示例
index.php

<?php

namespace Baijunyao\DesignPatterns\SimpleFactory;

require __DIR__.'/../vendor/autoload.php';

/**
 * 客户端
 *
 * Class Client
 * @package Baijunyao\DesignPatterns\SimpleFactory
 */
class Client
{
    /**
     * 不好的示例
     */
    public function bad()
    {
        $program = new Bad();
        $result = $program->getResult(1, '+', 2);
        echo $result;
    }

    /**
     * 不好的示例2
     *
     * @return int
     */
    public function bad2()
    {
        // 计算 1+2
        $operation = new Add();
        $operation->setNumberA(1);
        $operation->setNumberB(2);
        $result = $operation->getResult();
        echo $result;

        echo '<br>';

        // 计算 3+4
        $operation = new Add();
        $operation->setNumberA(3);
        $operation->setNumberB(4);
        $result = $operation->getResult();
        echo $result;
    }

    /**
     * 好的示例
     */
    public function good()
    {
        $factory = new Factory();
        $operation = $factory->create('+');
        $operation->setNumberA(1);
        $operation->setNumberB(2);
        $result = $operation->getResult();
        echo $result;
    }
}


$client = new Client();
$client->bad();
echo '<br>';
$client->bad2();
echo '<br>';
$client->good();

看似是很美好了;
然鹅;
如果我们要再增加新的运算;
那就需要改 Factory 增加 case ;
遇到可能性多的场景那 case 想想都可怕;
这就违反了开放封闭原则;
开放封闭原则简单来说就是对扩展开放对修改封闭;
理想情况下我们是不应该再改动 Factory 类的;
增加新运算应该使用扩展的方式;
这就是简单工厂的弊端了;
为此我们就需要了解下篇文章的工厂方法模式了;

github示例:https://github.com/baijunyao/design-patterns/tree/master/SimpleFactory

    • FR
白俊遥博客
请先登录后发表评论
  • latest comments
  • 总共10条评论
白俊遥博客

贝如 :PHPLAF v.1.0.10 正式发布了!本架构包依赖于ThinkPHP5.1 +Swoole4.2.8所研发。实现了在不影响原框架核心包的前提下,对Controller层加载进行了优化。使用Swoole对Websocket进行了架构定义与封装。架构包可以根据API 或 Socket的事件代码注释,自动生成调试器和开发文档。开发文档:https://blog.junphp.com/apiword.jsp

2020-04-24 13:41:11 回复

白俊遥博客

FR :讲的很好 ai了ai了

2020-04-24 10:30:29 回复

白俊遥博客

799135528 :博主的uml软件是什么

2018-09-06 15:11:37 回复

白俊遥博客

[( ̄3 ̄)] :点我让你很愉快

2018-08-23 14:28:40 回复

白俊遥博客

[( ̄3 ̄)] :点我让你很愉快:https://baijunyao.com/auth/oauth/logout

2018-08-23 14:27:40 回复

白俊遥博客

fanhanron :路过

2018-08-13 03:24:12 回复

白俊遥博客

wujunze :稳

2018-07-24 12:54:59 回复

白俊遥博客

那 記憶 擱淺 :大神你好,你是**的那个白俊遥?

2018-07-21 23:31:23 回复

白俊遥博客

. :**人?

2018-08-10 17:16:20 回复

白俊遥博客

Pmd :新疆?

2019-07-08 17:31:38 回复