3. php的设计模式:从支付开始谈工厂模式

日怪了,支付就支付呗,干嘛跟工厂模式扯在一起?且听我慢慢道来。

当今中国支付市场,可谓百花齐放。搞得开发人员痛苦不堪。来,咋们说说常用的支付就有:支付宝、微信、银联、百度钱包、Apple Pay、京东支付等等。有这么多,可苦了我们这些程序开发者了。各个支付接口之前参数不同,调用流程也不同。

接口太多的例子

下面的例子,我仅仅使用支付宝、微信进行说明。只是为了便于大家理解相关概念。

class 支付宝
{
    public function 即时到账($data)
    {
        // 即时到账接口
    }

    public function 移动支付接口($data)
    {
        // 移动支付接口
    }
}

class 微信支付
{
    public function APP支付($data)
    {
    }

    public function JS支付($data)
    {
    }

    public function 扫码支付($data)
    {
    }
}

假设上面是我们写好的lib。这种lib的提供简单暴力,仅考虑实现功能,对于易用性,易维护性均没有考虑。一般来说我们的应用,可能都需要支持网站上的支付、app上的支付。那么作为客户端,可能要许这样用(这里客户端是指:调用者

$alipay = new 支付宝();
// 构造网站支付的数据
$pcData = [];
$alipay->即时到账($pcData);

// 构造移动支付数据
$mobileData = [];
$alipay->移动支付($mobileData);

// 微信支付,再来一次
$wxpay = new 微信支付();
// 网站支付的数据
$wxpcData = [];
$wxpay->扫码支付($wxpcData);

// app支付
$appData = [];
$wxpay->APP支付($appData);

如果做过支付集成的同学,应该很清楚上面的过程。这里还有很多细节都一一放弃了,比如:不同接口签名方式不同、不同接口回调也不同,需要分别处理。蛋碎不蛋碎?

分析问题

如果仅仅从用的角度来说,上面的lib包也能够满足业务,因为支付这个东西,一般来说写好后,很好有人去动它。但是,现在的支付产品本身也在不断改进体验,可能一段时间后参数变了?一段时间后有新的支付方式出现?那么上面的代码,我们该怎么处理?

没错,只能修改代码,这就违背了 [开闭原则] 。而且一不小心,还可能把以前对的给改错了。

解决问题

要解决上面的问题,我采用这个方法:接口规范、工厂生产。

其实对于客户端,我并不关心具体的支付接口,对于理想的调用方式

$pay = PayFactory::getInstance(支付方式);

// 支付数据
$data = [];
$pay->charge($data);

对,就是上面这样就搞定了。我只是告诉工厂我想要一个什么支付的东西,然后给一个统一的数据格式,你帮我处理。我不再关心每个具体的接口参数名、不再关心具体的实例话过程。

我们来尝试用 简单工厂解决上面的问题

// 支付的数据,用于规范客户端的数据
class PayData
{
    public $orderNo;
    public $totalFee;
    public $subject;
    public $body;
    public $timeExpire;
}

class ChannelEnum
{
    const CHANNEL_IS_即时到账 = 'xxx';
    const CHANNEL_IS_移动支付 = 'xxx';
    // ... ...
}

interface ChargeInterface
{
    // 支付接口
    public function charge(PayData $data);
}

class 即时到账 implements ChargeInterface
{
    public function charge(PayData $data)
    {
        // 处理为数据
        // 发起支付
    }
}

class  移动支付 implements ChargeInterface
{
    public function charge(PayData $data)
    {
        // 处理为数据
        // 发起支付
    }
}

// ... ...

剩下的接口,就不再进行写了。大同小异,那么还差一个。客户端怎么进行实力化呢?难道还是用new?如果是这样,今晚跑题就太严重了。虽然现在我说的东西也有点超出范围了。哈哈

// 用一个静态工厂来解决客户端实例化的问题

class PayFactory
{
    public static function getInstance($channel)
    {
        $instance = null;
        swich ($channel) {
            case ChannelEnum::CHANNEL_IS_即时到账:
                $instance = new 即时到账();
                break;
            case ChannelEnum::CHANNEL_IS_移动支付:
                $instance = new 移动支付();
                break;
            // 其他实例化
        }

        return $instance;
    }
}

经过这些步骤,从某种程度上已经解决了 开闭 的问题。比如:现在我新增一个支付接口,添加的是一个类,然后在静态工厂中写一个switch分支。大大降低了风险。

另外客户端的调用复杂度也下降太多。所有的数据处理与签名,都放到具体的支付接口实现类中了。

但是细心的话,大家会发现,其实每次有修改,虽然对支付接口部分实现了扩展,关闭了修改。但是静态工厂类一旦有新的支付加入,还是会 违背 [开闭原则]

终于到主题了

哎,说了这么多废话,终于引出今晚的主题了。上面的方式叫做静态工厂模式,严格来说它不算一种设计模式。从他说起,是因为我们常常会用到它。而且也容易理解。

上面我们说了它违背了 开闭原则 ,那么这部代码我们怎么优化?这里就来了工厂类。用工厂类改写上面的静态工厂类,请注意区别哈。

abstract class Factory
{
    abstract public function getInstance();
}

class 立即到账Factory
{
    public function getInstance()
    {
        return new 立即到账();
    }
}

class 移动支付Factory
{
    public function getInstance()
    {
        return new 移动支付();
    }
}

// 其他工厂

看到了吧,对应每一个支付,就有一个工厂与之对应。如果有新的支付加入进来,新增加一个对应的工厂即可。

工厂类产生的新问题

虽然上面的代码解决了开闭原则。但是其实又给客户端的调用带来了新的复杂问题。用静态工厂的时候直接给一个参数就可以拿到对应的支付对象,现在这个判断的任务落在了客户端。

当然上面的问题也不是不能解决。大家可以去了解一下反射。在我上一篇文章 php的设计模式:单例模式 就用到了部分反射的知识,大家可以参考然后来试试更简单的调用,减少客户端的复杂度。

除了反射,针对php还是利用脚本语言的优势。比如类名可以写成变量,来进行对象的创建(其本质还是用到了反射)

这就完了

本来还有个抽象工厂模式,这货说实话,真正用的很少,至少我目前很少有项目需求需要设计的这么复杂。大家已经看到了,工厂模式的时候,已经会产生大量的类了,所以有时候在进行设计模式的运用时,我还是建议大家进行一个权衡。

另外针对上面所说的,我自己集成了一个支付宝、微信支付的开源项目。大家如果有需要的可以免费拿去用,如果觉得还不错的,可以打赏我的呃。

项目oschian地址 :支付宝、微信支付集成libary

项目github地址:支付宝、微信支付集成libary

这个项目发布一天,120个start。上了oschina的热门榜首。我自己所在的公司,也已经开始采用这个包。也欢迎大家提bug(当前并不是所有接口都经过严格测试,可能有坑,请小心)

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值