实例1:
<?php
interface transport{
public function go();
}
class bike implements transport{
public function go(){
echo "bike跑的慢";
}
}
class car implements transport{
public function go(){
echo "car开的快";
}
}
class bus implements transport{
public function go(){
echo "bus每一站都要停";
}
}
class transFactory{
public static function factory($transport){
switch($transport){
case 'bike':
return new bike();
break;
case 'car':
return new car();
break;
case 'bus':
return new bus();
break;
}
}
}
$transport = transFactory::factory('car');
$transport->go();
?>
实例2:
日怪了,支付就支付呗,干嘛跟工厂模式扯在一起?且听我慢慢道来。
当今中国支付市场,可谓百花齐放。搞得开发人员痛苦不堪。来,咋们说说常用的支付就有:支付宝、微信、银联、百度钱包、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)
{
// 处理为数据
// 发起支付
}
}
// ... ...
剩下的接口,就不再进行写了。大同小异,那么还差一个
终于到主题了
这里就来了工厂类,用来实例化各种支付接口。
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