设计模式 工厂模式

· 1572字 · 4分钟

工厂模式在平常业务代码中出现频率很高,仅次于单例模式了。一般在编写的代码的时候, 需要多个模块集中提供相同一种功能的服务 ,比如:短信发送(对接多个短信服务商)、支付(对接多个支付服务商)等业务时,我们通常会做一些代码设计思考:

  • 不想因为对接多个模块时把代码搞得乱糟糟的,像个面糊糊一样糊在一起

  • 不想写太多重复代码

  • 不想处理依赖搞的乱七八糟

  • 不想后续又对接了一个新的服务商而动现有跑的良好的代码等等

这个时候就可以思考是不是有已经有的设计模式可以使用,那么首先想到的就是 工厂模式,它可以解决以下几种情况:

  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责:你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则:无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。

但是它也有缺点,其中一个就是: 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 在现实中的例子比如短信发送这个功能,项目一开始只对接了七牛云和阿里云两个短信服务商,但是短信发送这一块算你们业务逻辑中比较重要的一环,所以你的boss希望多增加几个服务商来做备用,那么随着对接的服务商越来越多,你的工厂就越来越大,同时调用不同的服务商工厂部分的代码也可能充满了大量的if else 或switch 这样的判断,那么这个缺点可能会违背我们当初对代码结构设计的初衷了,当然后续会聊到其他设计模式去解决这个问题,当前我们先只讨论工厂模式的思想和实现。

目前很多博客或github上都有关于工厂模式的实例代码,但是没有几个能贴近现实业务,当然思想就是这么个思想,大家理解了思想那么example中不管是“动物厂”还是“手机厂”其实无所谓。下面的实例代码我选择了比较贴合web开发中的一个场景:手机信息发送来做example,希望能更好的帮助大家理解工厂模式。

工厂模式的思想很多博客有更好、更深入的讲解,这里不在班门弄斧。大家可以相关搜索引擎上搜索,这里直接展示代码。

<?php


/**
 * 工厂模式案例,以短信发送类作为案例
 * 案例中,假设使用阿里云和七牛云2个短信服务商
 * 
 *
 *       |-------------|                    |-------------|
 *       |   Factory   |    ---->           |   Product   |
 *       |-------------|                    |-------------|
 *              |                                   |
 *          |---------|                     |----------------|
 * |----------|   |----------|        |----------|       |----------|
 * | AFactory |   | BFactory |        | AProduct |       | BProduct |
 * |----------|   |----------|        |----------|       |----------|
 *     |                |
 * return AProduct      |
 *              return BProduct
 *
 * 为了方便演示,将所有代码写在同一个文件中,实际项目中不推荐次做法
 * 推荐目录结构为:
 * |-Sms
 * |--SmsFactory.php
 * |--SmsDriver.php
 * |--Factory
 * |----AliyunFactory.php
 * |----QiniyunFactory.php
 * |--SmsDriver
 * |----AliyunDriver.php
 * |----QiniuyunDriver.php
 */

abstract class SmsFactory {
    abstract public function driver() : SmsDriver;

    /**
     * 发送短信
     * @return mixed
     */
    public function sendSms() {
        return ($this->driver())->send();
    }

    /**
     * 获取当前短信驱动
     * @return mixed
     */
    public function getCurrentDriver() {
        return ($this->driver())->getDriver();
    }
}

/**
 * 阿里云短信工厂
 * Class AliyunFactory
 */
class AliyunFactory extends SmsFactory {
    private $phone, $content, $templateCode;

    public function __construct(string $phone,string $content,string $templateCode)
    {
        $this->phone = $phone;
        $this->content = $content;
        $this->templateCode = $templateCode;
    }

    public function driver(): SmsDriver{
        return new AliyunDriver($this->phone,$this->content,$this->templateCode);
    }

    // 做一些其他的 阿里云短信准备
    // 比如检查配置、检查可用性、依赖处理
}

/**
 * 七牛云短信工厂
 * Class QiniuyunFactory
 */
class QiniuyunFactory extends SmsFactory {

    private $phone, $content, $templateCode;

    public function __construct(string $phone,string $content,string $templateCode)
    {
        $this->phone = $phone;
        $this->content = $content;
        $this->templateCode = $templateCode;
    }

    public function driver(): SmsDriver{
        return new QiniuyunDriver($this->phone,$this->content,$this->templateCode);
    }

    // 做一些其他的 七牛云短信准备
    // 比如检查配置、检查可用性、依赖处理
}


interface SmsDriver {
    public function send();

    public function getDriver();
}

/**
 * 阿里云短信驱动
 * Class AliyunDriver
 */
class AliyunDriver implements SmsDriver {
    /** @var string 短信模板 */
    private $templateCode = '';
    /** @var string 发送的手机号 */
    private $currentPhone = '';
    /** @var string 发送的信息内容 */
    private $content = '';

    public function __construct(string $phone,string $content, string $templateCode = '')
    {
        $this->templateCode = $templateCode;
        $this->currentPhone = $phone;
        $this->content = $content;
    }

    public function send() {
        // 实际组装请求数据,
        // 向服务网关发送数据请求
        // 处理返回、报错
    }

    /**
     * 对接发送接口
     */
    private function curl() {}

    public function getDriver()
    {
        return __CLASS__;
    }
}

/**
 * 七牛云短信驱动
 * Class QiniuyunDriver
 */
class QiniuyunDriver implements SmsDriver {

    /** @var string 短信模板 */
    private $templateCode = '';
    /** @var string 发送的手机号 */
    private $currentPhone = '';
    /** @var string 发送的信息内容 */
    private $content = '';

    public function __construct(string $phone,string $content, string $templateCode = '')
    {
        $this->templateCode = $templateCode;
        $this->currentPhone = $phone;
        $this->content = $content;
    }

    public function send() {
        // 实际组装请求数据,
        // 向服务网关发送数据请求
        // 处理返回、报错
    }

    public function getDriver()
    {
        return __CLASS__;
    }
}

/**
 * 验证码类
 * Class SendCaptcha
 */
class SendCaptcha {

    public $phone;

    public function __construct(string $phone)
    {
        $this->phone = $phone;
    }

    public function send(SmsFactory $factory) {
        // 创建验证码
        $captcha = $this->createCaptcha();
        // 存储验证码
        $cacheKey = 'cacha_captcha_'.$this->phone;

        // 选择短信的驱动类
        // 实例使用阿里云
        $aliyunTemplateCode = '32123123';
        $res = $this->run(new AliyunFactory($this->phone,$captcha,$aliyunTemplateCode));
        if (!$res) {
            return false;
        }

        $this->cache($cacheKey,$captcha);
        return true;
    }

    /**
     * 缓存类
     * @param string $key
     * @param string $value
     * @return bool
     */
    private function cache(string $key, string $value) {return true;}

    private function run(SmsFactory $factory) {
        return $factory->sendSms();
    }

    private function createCaptcha() {
        return mt_rand(111111,999999);
    }
}