Can
Be Better

php设计模式:策略模式的实际应用

序言

过去曾经写过C语言的面向过程的程序,其实当时就很想吐槽:好多 if else好烦啊,尤其是if里面嵌套着if……然后修改其中某一个if条件就要修改所有if条件,然后内存溢出了,然后就要想办法优化逻辑与变量类型(4位~32位单片机真的不爽啊)。

所以说,面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上。

传统思维习惯分析

为什么会业务逻辑层使用if else,其实使用者的目的也是为了重用,但是这是面向过程编程的重用,程序员只看到代码重用,因为他看到if else几种情况下大部分代码都是重复的,只有个别不同,因此使用if else可以避免重复代码,并且认为这是模板Template模式。

他范的错误是:程序员只从代码运行顺序这个方向来看待它的代码,这种思维类似水管或串行电路,水沿着水管流动(代码运行次序),当遇到几个分管(子管),就分到这几个分管子在流动,这里就相当于碰到代码的if else处了。

而使用OO,则首先打破这个代码由上向下顺序等同于运行时的先后循序这个规律,代码结构不由执行循序决定,由什么决定呢?由OO设计;设计模式会取代这些if else,但是最后总是由一个Service等总类按照运行顺序组装这些OO模块,只有一处,这处可包含事务,一般就是Service,EJB中是Session bean。

一旦需求变化,我们更多的可能是Service中各个OO模块,甚至是只改动Service中的OO模块执行顺序就能符合需求。

这里我们也看到OO分离的思路,将以前过程语言的一个Main函数彻底分解,将运行顺序与代码其他逻辑分离开来,而不是象面向过程那样混乱在一起。所以有人感慨,OO也是要顺序的,这是肯定的,关键是运行顺序要单独分离出来。

是否有if else可以看出你有没有将运行顺序分离到家。

策略模式的定义解析

【策略模式解释】

策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

这是书本上给的定义,是不是完全搞不懂?我结合支付,再来给你解释一下,一定就赫然开朗啦!

首先是这句 让算法独立于使用它的客户而变化 (我是倒着在分析哦)。
这是什么意思?也就是说实现一个功能,有多个方法,而选择这个方法的控制权不要交给客户端,也就说了,我换了实现方法,客户端是不需要改代码的。

那么要做到这样子,必然提供给客户端的一个稳定的调用类(称为环境类),首先调用这个类能够产生一个具体算法的实例,其次这个调用类,还需要公布一个接口,让客户端调用实现具体功能。

那么做到以上,无论实现多少种双方,客户端的调用都是不变的。控制权都在这个调用类里边,由它来决定到底采用哪种算法。

下面来接着说算法部分。如果需要 环境类 提供一个实现具体功能的接口,那么这些算法必然实现了一个公共接口(称为抽象策略类)。才能确保有相同的方法提供出来。然后具体的算法都要实现这个接口。这也就是上面定义中的 将每一个算法封装起来 每一个具体的算法称为:具体策略类

【策略模式中主要角色】

抽象策略(Strategy)角色:定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现。Context使用这个接口来调用其ConcreteStrategy定义的算法
具体策略(ConcreteStrategy)角色:以Strategy接口实现某具体算法
环境(Context)角色:持有一个Strategy类的引用,用一个ConcreteStrategy对象来配置

【策略模式的优点和缺点】

策略模式的优点:
1、策略模式提供了管理相关的算法族的办法
2、策略模式提供了可以替换继承关系的办法 将算封闭在独立的Strategy类中使得你可以独立于其Context改变它
3、使用策略模式可以避免使用多重条件转移语句。

策略模式的缺点:
1、客户必须了解所有的策略 这是策略模式一个潜在的缺点
2、Strategy和Context之间的通信开销
3、策略模式会造成很多的策略类

【策略模式适用场景】

1、许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法
2、需要使用一个算法的不同变体。
3、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构
4、一个类定义了多种行为,并且 这些行为在这个类的操作中以多个形式出现。将相关的条件分支移和它们各自的Strategy类中以代替这些条件语句

【策略模式与其它模式】

Template模式:模板方法模式与策略模式的不同在于,策略模式使用委派的方法提供不同的算法行为,而模板方法使用继承的方法提供不同的算法行为
享元模式(flyweight模式):如果有多个客户端对象需要调用 同样的一睦策略类的话,就可以使它们实现享元模式

 

不知道这个解释大家清楚定义了没有,如果还不清楚,看类图

类图演示

策略模式包含的角色如下:

  • Context: 环境类
  • Strategy: 抽象策略类
  • ConcreteStrategy: 具体策略类

这下子是不是很清楚了?策略模式是使用非常广泛的一个设计模式。他很好的提现了:控制反转、依赖注入等思想。有同学说,不想看文字,有本事上代码呀!嗯,我喜欢,新鲜出炉的代码来了

策略模式PHP代码实现

在整个模式中,Strategy 起着承上启下的作用。我就先来实现它

interface ChargeStrategy
{
    public function charge();
}

OK,抽象策略类就完成了,他的主要目的就是规范一个必须要实现的方法,环境类依赖这个接口进行编程。

下面接着写算法的实现。还是以支付宝支付、微信支付为例。对于用户来说他要实现的功能是支付。那么支付又有多种选择(多种算法)。但是客户端不需要做出选择,他把这个权利让 环境类 去选择。这样子客户端就简单了。所有的算法需要实现 策略类接口。

// 支付宝策略类
class AliCharge implements ChargeStrategy
{
    public function charge()
    {
        // 完成支付宝的相关逻辑
    }
}

// 微信策略类
class WxCharge implements ChargeStrategy
{
    public function charge()
    {
        // 完成微信的相关逻辑
    }
}

这里声明一下,这里为了纯粹的把 策略模式 讲明白,抛开了很多细枝末节,真正的支付中的实现,大家可以去看看项目的源代码。

final class ChargeContext
{
    /**
     * @var ChargeStrategy $charge
     */
    private $charge;

    public function initInstance($channel)
    {
        if ($channel == 'ali') {
            $this->charge = new AliCharge;
        } elseif ($chananel == 'wx') {
            $this->charge = new WxCharge;
        } else {
            $this->charge = null;
        }
    }
    
    public function charge()
    {
        if (is_null($this->charge)) {
            exit('初始化错误');
        }
    
        $this->charge->charge();
    }
}

以上就基本完成了,而对于客户端来说,就非常简单啦。

// 获取用户选择的支付方式
$channel = trim($_GET['channel']);

$context = new ChargeContext();

// 初始化支付实例
$context->initInstance($channel);

// 调用功能
$context->charge();

代码写完了,不知道大家有没有感受到好处,这个模式很好的实现了开闭原则。比如说:现在新增加了一个PayPal支付方式。那么只需要添加一个PayPal的策略算法。在ChargeContext中把对应的实例初始化加进去,其他地方都不需要动的。

体会

最后再说几句,不知道大家注意到没有,在 ChargeContext 这个类中,其实还使用了 简单工厂 这个模式。这里想给大家说明的是,其实设计模式只是一些编码的技巧,完全可以自由搭配组合,基本思想就是 设计模式的六大原则

当然,实际编码中也没有必要非要都实现这六大原则。这个也没有什么规范,只能大家多去实践,然后自己约定出一套适合业务的规范就好。

文章来源:

https://helei112g.github.io/2016/07/23/php%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8/

其他参考代码

1

<?php
/**
* PHP设计模式之策略模式实例
*/

/**
* 抽象策略类(Strategy):定义所有支持的算法的公共接口。
*/
interface ProductStrategy {
public function push_product();
}

/**
* 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
*/
class ManProduct implements ProductStrategy {

    public function push_product(){
        /**
         * code...
         */
        return '男的都喜欢数码产品、户外产品';
        }
    }

class WomanProduct implements ProductStrategy {

    public function push_product(){
        /**
         * code...
         */
        return '女的都喜欢衣服、包包、化妆品';
    }
}

class RandomProduct implements ProductStrategy {

    public function push_product(){
        /**
         * code...
         */
        return '吃的,喝的';
    }
}

/*
* 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。
* 最后,客户端调用,比较好得做法是,我们使用一个简单工厂返回策略的实例。
* 这样就不用在各个需要调用策略的地方,去初始化某个具体的数据库对象了
* 只要调用全局的工厂方法去返回一个实例
*/

class ProductFactory{
    //缓存我们已经实例化过的类,类似单例模式,但不是很严格
    private static $instance;

    private static $userType = 'Woman';

    //这里具体返回哪个策略的实例,根据运行时条件进行,比如当前登陆的是女性:self::$userType = 'Woman';
    public static function getInstance(){
        if (!(self::$instance instanceof self) ){
            if (self::$userType == 'Man'){
                self::$instance = new ManProduct();
            }elseif (self::$userType == 'Woman'){
                self::$instance = new WomanProduct();
            }else {
                self::$instance = new RandomProduct();
            }
        }
        return self::$instance;
    }
}

/*
* 硬编码的方式
* 有多少地方使用,就需要写多少次
*/
$userType = 'Man';

if($userType == 'Man') {
    $Product = new ManProduct();
} else if($userType == 'Woman') {
    $Product = new WomanProduct();
} else {
    $Product = new RandomProduct();
}
echo $Product->push_product();

/**
* 策略模式使用
* 客户程序只管调用实例化策略的产品工厂类,无需关系其他的
* 即使日后需要增加一个新的策略,客户端所有使用这个策略的程序什么都不用改动
*/

$product = ProductFactory::getInstance();
echo $product->push_product(); //结果;女的都喜欢衣服、包包、化妆品
?>

2

<?php


/**
 * 抽象策略角色,以接口实现
 */
interface Strategy {
 
    /**
     * 算法接口
     */
    public function algorithmInterface();
}
 
/**
 * 具体策略角色A
 */
class ConcreteStrategyA implements Strategy {
 
    public function algorithmInterface() {
        echo 'algorithmInterface A<br />';
    }
}
 
/**
 * 具体策略角色B
 */
class ConcreteStrategyB implements Strategy {
 
    public function algorithmInterface() {
        echo 'algorithmInterface B<br />';
    }
}
 
/**
 * 具体策略角色C
 */
class ConcreteStrategyC implements Strategy {
 
    public function algorithmInterface() {
        echo 'algorithmInterface C<br />';
    }
}
 
/**
 * 环境角色
 */
class Context {
    /* 引用的策略 */
    private $_strategy;
 
    public function __construct(Strategy $strategy) {
        $this->_strategy = $strategy;
    }
 
    public function contextInterface() {
        $this->_strategy->algorithmInterface();
    }
 
}
 
/**
 * 客户端
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
        $strategyA = new ConcreteStrategyA();
        $context = new Context($strategyA);
        $context->contextInterface();
 
        $strategyB = new ConcreteStrategyB();
        $context = new Context($strategyB);
        $context->contextInterface();
 
        $strategyC = new ConcreteStrategyC();
        $context = new Context($strategyC);
        $context->contextInterface();
    }
 
}
 
Client::main();
?>

 

不开启评论,如有问题疑问请发邮件。[email protected]最长的路 » php设计模式:策略模式的实际应用

评论 抢沙发

评论前必须登录!