2、策略模式
策略模式:
策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化(策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理)。
使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类( ConcreteStrategy)中提供。
由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出现变化时,只需要实现新的策略类,并在客户端登记即可。
策略模式相当于"可插入式(PtUggable) 的算法"。
这个模武涉及到三个角色:
环境(Context) 角色:持有一个Strategy类的引用(上下文对象),负责和具体的策略类交互。
抽象策略(Strategy) 角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy) 角色:包装了相关的算法或行为。
UML图:
例(商场促销):
环境角色:
class CashContext {
private CashSuper cs;
public CashContext(CashSuper csuper) {
this.cs = csuper;
}
public double GetResult(double money) {
return cs.acceptCash(money);
}
}
客户端部分代码:
CashContext cc = null;
switch(cbxType.SelectedItem.ToString())
{
case "正常收费":
cc = new CashContext(new CashNormal());
break;
case "满300返100":
cc = new CashContext(new CashReturn("300", "100"));
break;
case "打8折":
cc = new CashContext(new CashRebate("0.8"));
break;
}
但是仅仅采用策略模式又变成了客户端去判断的老路,那么我们可以采用策略模式+简单工厂模式将这个判断过程从客户端移走。
此时环境角色:
using System;
using System.Collections.Generic;
using System.Text;
namespace 商场管理软件
{
//现金收取工厂
class CashContext
{
CashSuper cs = null;
//根据条件返回相应的对象
public CashContext(string type)
{
switch (type)
{
case "正常收费":
CashNormal cs0 = new CashNormal();
cs = cs0;
break;
case "满300返100":
CashReturn cr1 = new CashReturn("300", "100");
cs = cr1;
break;
case "打8折":
CashRebate cr2 = new CashRebate("0.8");
cs = cr2;
break;
}
}
internal CashSuper CashSuper
{
get => default(CashSuper);
set
{
}
}
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
}
综上:
什么情况下应当使用策略模式:
1.出现同一个算法,有很多不同的实现的情况,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
2.出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况,可以使用策略模式来代替这些条件语句。
优点:
1.策略模式可以避免让客户端涉及到不必要接触到的复杂的只与算法有关的数据。
2.可以以相同方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
3.避免使用难以维护的多重条件选择语句
4.更好的扩展
缺点:
1.上述策略模式,把分支判断又放回到客户端,要改变需求算法时,还是要去更改客户端的程序
2.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别你以便适时选择恰当的算法类。
3.增加了对象的数目
4.只适合扁平的算法结构
本质:
分离算法,选择实现。
简单工厂模式与策略模式对比:
简单工厂模式(计算器问题):输入运算符号,工厂实例化出合适的对象,通过多态,返回父类的方式实现计算器的结果。
但简单工厂模式只是解决了对象的创建问题,而遇到多次改动的情况(如商场打折问题),每次维护和扩展都要多次修改这个工厂,以致代码需要重新编译部署,对于这类问题简单工厂模式就变成了很糟糕的处理模式。面对算法的时常变动,我们引出了策略模式。
策略模式:以抽象策略为例:
1.Strategy类:定义所有支持的算法的公共接口,通过构造方法,传入具体的策略,再根据策略的不同获取不同的处理方式。
2.ConcreteStrategy类,封装了具体的算法和行为,继承于Strategy。简单来讲:简单工厂模式就是用来创建对象的,策略模式就是用来封装算法的,两者虽然看着相似,但是侧重点有所不同。
我们对于商店收银台系统,我们可以结合简单工厂模式和策略模式,通过把“工厂”建立在Context类中,加工出来的对象,可供直接使用,实现实例化具体策略的过程由客户端转移到Context类(对应抽象策略的Strategy类)中。
不足:
模式搭配仍然还有缺陷,CashContext类中还是用到了switch,也就是说,如果我们需要增加一种算法,比如“满200减50”,我们就必须要更改CashContext来实现新的要求(任何需求的变更都是需要成本的)(但是成本的高低是有所不同的,面对同等的需求,当然是改动越小越好)。这个问题可以使用反射技术来得到更好的解决,反射技术将会在以后的篇目中进行讲解。