大话设计模式 | 2. 策略模式
《 》是作者「程杰」通过趣味的场景设置,以诙谐的表达来解读和剖析「面向对象」编程思维和「设计模式」。书中的示例代码是以 .NET 的 C# 语言编写而成。
本文是我对《大话设计模式》的学习系列笔记的第二篇,策略模式。
1. 定义
策略模式 (Strategy Pattern),定义了算法家族,分别封装起来(封装变化点),让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式减少了各种算法类与使用算法类之间的耦合,使得:
2. 组成
与简单工厂模式,策略模式主要是由三个角色组成:
「抽象策略(Strategy)」角色,是「具体策略」的父类,定义所有支持的算法的公共接口。
「具体策略(ConcreteStrategy)」角色,封装了具体的算法或行为。
「环境(Context)」角色,用一个具体策略来配置,维护一个对抽象策略对象的引用。

3. 使用步骤
主要包含四步:

4. 实例
《大话设计模式》中,是通过一个商场的收银系统为例进行说明的。商场收银系统需要能够处理正常收费、商品打折和节假日满减等各种活动。
这里正常收费、打折和满减是该系统需要实现的具体算法,他们都共用的一个收费的接口。
创建抽象策略类
创建 的抽象策略类,并定义所支持的算法的公共接口 。
abstract class CashSuper
{
public abstract double acceptCash(double money);
}
创建具体策略类
创建具体策略类(、、 等),来继承 的抽象策略类,并实现 方法,来定义具体的算法。
class CashNormal : CashSuper
{
public override double acceptCash(double money)
{
return money;
}
}
class CashRebate : CashSuper
{
private double moneyRebate = 1;
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double acceptCash(double money)
{
return money * moneyRebate;
}
}
class CashReturn : CashSuper
{
private double moneyCondition = 0;
private double moneyReturn = 0;
public CashReturn(string moneyCondition, string moneyReturn)
{
this.moneyCondition = double.Parse(moneyCondition);
this.moneyReturn = double.Parse(moneyReturn);
}
public override double acceptCash(double money)
{
double result = money;
if (money >= moneyCondition)
{
result = money - Math.Floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
创建环境类
通过构造方法,传入具体的收费策略,并根据具体的策略对象来调用其算法,得到不同的计算结果。
class CashContext
{
private CashSuper cashsuper; //声明一个CashSuper对象
public CashContext(CashSuper cashsuper)
{
this.cashsuper = cashsuper;
}
public double GetResult(double money)
{
return cashsuper.acceptCash(money);
}
}
调用环境类的方法
在主函数中(客户端),通过传入相应的策略对象,并调用环境类的 方法,来得到收费的结果,让具体的算法与客户端进行了隔离。
但是,这里存在一个很大的问题,虽然具体的算法与客户端进行了隔离,但是还是要在客户端去判断需用用哪一个算法。
...
CashSuper cashsuper = null;
switch (type)
{
case "0":
cashsuper = new CashContext(new CashNormal());
break;
case "满百返百":
cashsuper = new CashContext(new CashReturn());
break;
case "8折":
cashsuper = new CashContext(new CashDebate());
break;
}
double totalPrice = 0;
totalPrice = cashsuper.GetResult(price * number);
total += totalPrice;
...
与简单工厂模式结合
在简单工厂模式中,我们是通过创建工厂类,并定义静态方法,通过传入不同参数来创建不同具体产品类的实例。
那么,我们也可以将实例化的具体策略过程,通过简单工厂的应用,从客户端转移到环境类中。
class CashContext
{
CashSuper cashsuper = null;
public CashContext(string type) //这里参数由原来的具体策略对象,变成了收费类型的字符串
{
switch (type)
{
case "0":
CashNormal cashNormal = new CashNormal();
cashsuper = cashNormal;
break;
case "满百返百":
CashReturn cashReturn = new CashReturn("300", "100");
cashsuper = cashReturn;
break;
case "8折":
CashRebate cashRebate = new CashRebate("0.8");
cashsuper = cashRebate;
break;
}
}
public double GetResult(double money)
{
return cashsuper.acceptCash(money);
}
}
相应的,客户端代码就比较简单了,只需调用环境类,传入收费类型即可。
...
Console.Write("Please input the discount: 0/满百返百/8折 ");
string discount = Console.ReadLine();
CashContext cashsuper = new CashContext(discount);
double totalPrice = 0;
totalPrice = cashsuper.GetResult(price * number);
total += totalPrice;
...
在客户端的代码中,对比简单工厂模式,只需认识一个 类就可以了。而如果通过简单工厂模式的话,客户端需要认识 和 两个类。这使得具体的收费算法彻底与客户端分离了。

5. 参考
本文参考了这两篇文章: