策略模式与模板方法模式的介绍与对比

最近在给公司的商城作第三方支付的对接,看了一下之前的微信支付,感受结合了一下以前看的设计模式,想试试能不能在上面用上。一番研究后,感受也是能够,就是可能有点牛刀小试。php

模式定义

  1. 策略模式的定义
    策略模式定义了算法族,分别封装起来,让它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。
  2. 模板方法模式定义
    模板方法模式在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类能够在不改变算法结构的状况下,从新定义算法中的某些步骤。

ROUND 1:多支付方式

一、 场景描述算法

微信支付有预下单、退款、订单查询等等接口,看了下第三方支付,一样也是相似的三板斧。很容易,咱们会想到如下这个类图:

clipboard.png


图中能够看到WxPay和SandPay都继承了Pay,他们有一样的行为:下单、退款、订单查询,可是,咱们能够知道他们的实现确定是不同的,这样咱们都必须覆盖父类的方法,来处理不同支付的细节,这样的时候,代码就会显得冗余,每一个子类都须要覆盖父类的实现,如何让代码更加统一呢?

二、 问题分析编程

这个时候,咱们须要的是将行为独立出来,让其封装在特定的行为类里,这样,咱们就能“指定”行为到支付的实例。好比说,咱们想要产生一个新的第三方支付实例,咱们能够动态的让其实现微信支付的下单操做(固然这是不合理)。

这时,咱们用到了一个很重要的设计原则:设计模式

针对接口编程,而不是针对实现编程

咱们用接口表明每一个行为,好比说,OrderBehavior和RefundBehavior,行为的每一个实现都讲实现其中一个接口。支付类不会实现下单和退款的接口,而是有其余类专门实现。咱们称这种类叫“行为”类。和之前作法不同的地方在于,之前的作法是:行为由超类或者子类继承某个接口,自行实现。这两种作法都依赖与“实现”,这样咱们很容易被实现绑死,很难在后来改变行为(除非写更多的代码)。新的设计里,支付类将使用由接口所表示的行为,因此实际的“实现”并不会绑死在支付类中,这样,支付类就不用在了解行为实现的细节。微信

来看一下新的类图:

clipboard.png

以及,看看Pay抽象类中统一后的方法代码:微信支付

<?php

class Pay{

    public $orderBehavior;
    public $refundBehavior;
    
    public function orderPay()
    {
        return $this->orderBehavior->orderPay();
    }
    
    public function refund()
    {
        return $this->refundBehavior->refund();
    }

}

而后在实现的时候,子类就能这样动态实现this

<?php

//实例化微信支付
$pay = new WxPay();
//赋值微信下单实例
$pay->orderBehavior = new WxOrder();
//实现了微信支付的下单
$pay->orderPay();

//第三方支付同理
$pay = new SandPay();
$pay->orderBehavior = new SandOrder();
$pay->orderPay();

可能这时候有人会问,我明明能够直接就在具体类里实现这个方法,也不用多写这么多类与接口,正常来讲,支付也不会改变其实现方式。
是的,通常来讲,支付是不会修改的,可是若是忽然说如今不用第三方支付,所有都用微信支付,那么咱们代码的修改可能会不少了,可是用这种实现咱们只须要改动一个很小地方:spa

<?php

//在外部看起来咱们调用的是第三方支付
$pay = new SandPay();
//真实咱们的实现是微信支付
$pay->orderBehavior = new WxOrder();
//咱们因为不用管实际的细节,也不用作太多的代码改动,并且由微信支付原本的稳定实现,咱们也能很放心的说,代码不会出现bug
$pay->orderPay();

这一种实现方式,也符合了一个设计原则:设计

多用组合,少用继承

这里支付对下单甚至退款的操做,实际的实现都不是经过继承获得的,而是经过将其余类的结合,不只能够将算法族封装成类,更能够“在运行时动态改变行为”,只要行为对象符合正确的接口标准便可。code

这个状况,你以为是用了哪一种设计模式?


ROUND 2:一样的实现结构

一、 场景描述

在处理完多种支付的场景后,咱们在开始加入了第三方支付的代码,在写的过程发现,下单操做和微信支付相似,咱们须要在请求
以前,组装请求必要参数、签名,获得返回结果后,对数据验签,而后进行本身的业务逻辑,最后返回一个应答。
  同理,退款操做也是同样:组装请求必要参数、签名等等,和下单操做几乎处理逻辑的顺序或者说结构很类似,若是咱们继续这么
编写,会发现不少重复代码,这个时候要怎么处理呢?

二、 问题分析

在解决问题以前,先说一下一个重要的设计原则:
找出应用中可能须要变化之处,把它们独立出来,不要和那些不须要变化的代码混在一块儿。
通俗来讲,就是要善于发现现实中的变与不变,抽离不变的地方,使其能复用,而后让变化的部分自行解决处理。
  在这个场景中,很特殊的,咱们发现不论是微信的下单、退款,仍是第三方的下单、退款,他们业务的逻辑几乎是一个流水线上出
来的,就是他们不变的地方,而变化的是什么呢?很明显就是具体的实现业务不一样,这部分应该由业务本身实现。
  这样,咱们将该描述用类图表示以下:

clipboard.png

而代码具体以下:

<?php

abstract class PayAction{

    public function doAction()
    {
        $this->generateRequestData();
        $this->generateSign();
        $this->request();
        $this->verifiedSign();
        $this->handleResponse();
        $this->returnMsg();
    }

    abstract protected function generateRequestData();
    
    abstract protected function generateSign();
    
    abstract protected function request();
    
    abstract protected function handleResponse();
    
    abstract protected function verifiedSign();
    
    abstract protected function returnMsg();
}

class WxOrder extends PayAction{

    public function orderPay()
    {
        $this->doAction();
    }
}

只针对这一系列操做,咱们能够写成这个样子,这样全部的行为都同样了,只须要实现自身不同的地方便可。

ROUND 3:结合的效果

一、若是把第一种状况的结构和第二种状况的结构,结合起来,最后是什么样子呢,咱们能够看一下:

clipboard.png

这样,整个结构就很清晰了,客户端调用实现了行为的下单或者退款类,不须要知道具体的细节,而且能够动态的改变行为的对象;而行为的操做的重复部分又被抽出来,只须要各自实现变化的部分,不变的部分都由抽象类PayAction先定义好,再由不同的支付类实现公共的部分:签名与验签,甚至最后固定的返回正确和错误时的格式都是同一个支付里,相同的部分;最后不同的只有不一样请求时候,不同的请求参数,以及得到返回参数后的不同的处理了。
类和接口的确比一开始的设计要多了很多,可是结构很是清晰,并且修改的时候,能够颇有针对性的修改,对于客户端来讲,调用是透明的,对于提供服务的咱们来讲,不论是新增一个支付方式,仍是多了一个支付操做,咱们均可以很好的增长代码,不用修改现有代码,而这个也是一个常常说到的设计原则:

开闭原则:对修改关闭,对扩展开放

ROUND 4:解答与对比

第一和第二个场景,它们分别对应的设计模式是什么?

第一种设计模式是:策略模式,而第二种是:模板方法模式

第一种咱们能够清楚的看到,咱们对于如何运行一个行为的时候,咱们是将其封装在一个类中处理,而且,它们能够相互替换。算法的改变由客户来决定,能够动态的改变。第二种咱们能够看到的是,整个算法的结构已经被定义好,跟着预约好的模板来编写咱们的算法,就能够实现类似的功能有条不紊的编写下去,不会出现多余的部分,而且能够专心的处理自身特别的业务逻辑。因此,之后若是代码的相关结构很类似,能够选用模板方法模式来编写;若是业务中的某些行为能够被抽象而且有须要动态改变的时候,能够考虑策略模式来编写。

相关文章
相关标签/搜索