因为最近在学策略模式,所以想先跳过创建型设计模式中得适配器模式

定义

策略模式,顾名思义,就是提供多个策略的模式,用户在不同的情况下可以选择不同的策略,比如商场的打折策略(不同节假日不同的折扣方式),旅游出行的方式(提供飞行,或者火车,或者大巴的方式)。再进一步讲,就是把这些同一个系列的不同的算法封装起来,让它们能够被客户自由地使用。

UML图

Drawn by StarUML

clipboard.png

实例

大学课程(Lesson)中,有这样的两种课程,一种演讲(Lecture),一种研讨会(Seminar),两种都需要进行收费,并且有不同的收费机制(chargeType),前者是固定收费(FixedCost),后者则是按小时收费(TimeCost)

CostStrategy.php

<?php
//CostStrategy类,抽象策略类,Context通过调用这个类来调用该类的具体策略A、B类
//实际上是通过抽象类或者接口封装算法的标识,我们把封装算法的接口称作策略
abstract class CostStrategy{
    //定义收费策略的抽象方法,课程与收费机制
    
    abstract function cost(Lesson $lesson);
    abstract function chargeType();
}

?>

TimeCostStrategy.php

<?php
//TimeCostStrategy类,相当于UML图中的ConcreteStrategyA
class TimeCostStrategy extends CostStrategy{

    //按时长消费,调用上下文中得getDuration()获取课程时长,并定义每小时消费5
    public function cost(Lesson $lesson){
        return ($lesson->getDuration() * 5);  
    public function chargeType(){
        return 'Hourly rate';
    }

}
?>

FixedCostStrategy.php

<?php
//FixedCostStrategy类,相当于UML图中得ConcreteStrategyB
class FixedCostStrategy extends CostStrategy{
    public function cost(Lesson $lesson){
        return 30;        //固定收费30
    }
    public function chargeType(){
        return 'Fixed rate';
    }

}
?>

lesson.php

<?php
//Lesson类,相当于UML图中得Context上下文类
class Lesson{
    public $duration;        //定价策略为TimeCostStrategy时的时长
    public $strategy;
    public function __construct($duration,CostStrategy $costStrategy){
        $this->duration = $duration;
        $this->strategy = $costStrategy;
    }
    public function cost(){
        return $this->strategy->cost($this);
    }
    public function chargeType(){
        return $this->strategy->chargeType();
    }
    public function getDuration(){
        return $this->duration;
    }

    //其它方法
}
?>

Lecture.php

<?php
//Lecture类,是对上下文(Lesson)的继承
//UML图中没有标注出上下文的子类,因此设计模式要具体情况具体分析
class Lecture extends Lesson{

}
?>

Seminar.php

<?php
//Seminar类,对上下文的继承
class Seminar extends Lesson{

}
?>

Client.php

<?php
//一次性将所有需要用到的类都引用进来
function __autoload($classname){
    $classpath = "./".$classname.".php";
    if (file_exists($classpath)) {
        require_once($classpath);
    }
}

$Seminar = new Seminar(4,new FixedCostStrategy());
echo 'The cost type is '.$Seminar->chargeType().',and the cost is '.$Seminar->cost();
?>

优点

  1. 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪一个类

  2. 策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例

  3. 消除冗余的if..else...语句

缺点

  1. 客户端需要知道每一个策略类,并且知道这些类有什么不同,因此要在策略行为与客户行为密切相关的时候才使用这种模式,把策略暴露给客户

  2. 策略模式会产生很多策略,因此开销问题也是需要考虑的问题

  3. Context与Strategy之间不必要的通信开销。ConcreteStrategy类共享Strategy,因此需要实现Strategy中的所有抽象方法,如果有的具体策略类比较简单,但还是必须要去实现它的抽象方法,因此会增加不必要的开销

参考:
1.设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
2.深入PHP:面向对象、模式与实践(第3版)


R_Jeff
405 声望21 粉丝

坚持拍黄片的大四网页狗要成为PHPer