概念
所谓的策略模式,它封装了一族算法,使它们之间可以互相的替换,而对使用算法的用户来说不会受到影响,其实,说不受到影响是指用户代码的模式是一致的,即不用改变使用习惯。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。策略模式的设计原则即把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。
策略模式的组成
抽象策略角色(Strategy):
定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略角色(ConcreteStrategy):
包装了相关的算法和行为。
环境角色(context):
持有一个策略类的引用,最终给客户端调用(用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据)。
Context(应用场景):
1、需要使用ConcreteStrategy提供的算法。
2、内部维护一个Strategy的实例。
3、负责动态设置运行时Strategy具体的实现算法。
4、负责跟Strategy之间的交互和数据传递。
如下图所示
优缺点
优点:
1、简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
2、避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点:
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
2、在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。(这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化的减轻了客户端的压力。)
简单工厂模式和策略模式的比较
个人举得这个策略模式和简单工厂模式一样。简单工厂模式中有一个工厂类,负责根据输入参数的不同来生成不同的子类,并将生成的子类作为基类返回(这样可以让客户端在使用的时候很方便)。客户端只需要调用工厂类创建一个基类的实例,然后调用这个实例的函数来实现自己的功能。而策略模式中的Context类的功能基本上类似,它也负责根据输入参数来生成不同的类,只是它并不返回生成类,而是将生成类所实现的功能接口包装一次,提供给客户。这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。
使用
当存在以下情况时使用Strategy模式
1)许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
2) 需要使用一个算法的不同变体。
3)算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)一个类定义了多种行为 ,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句
案例
///<summary>
/// 现金收费抽象类
/// </summary>
abstract class CashSuper
{
//现金收取超类的抽象方法,收取现金,参数为原价,返回当前价。
public abstract doubleacceptCash(double money);
}
/// <summary>
/// 正常收费子类
/// </summary>
class CashNormal:CashSuper
{
public override doubleacceptCash(double money)
{
return money;
}
}
/// <summary>
/// 打折子类
/// </summary>
class CashRebate:CashSuper
{
private double moneyRebate = 1d;
public CashRebate(string moneyRebate)
{
this.moneyRebate =double.Parse(moneyRebate);
}
public override doubleacceptCash(double money)
{
return money * moneyRebate;
}
}
/// <summary>
/// 返利子类
/// </summary>
class CashReturn:CashSuper
{
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
/// <summary>
/// 返利子类构造方法
/// </summary>
/// <paramname="moneyCondition">返利标准</param>
/// <paramname="moneyReturn">返利值</param>
public CashReturn(stringmoneyCondition, string moneyReturn)
{
this.moneyCondition =double.Parse(moneyCondition);
this.moneyReturn =double.Parse(moneyReturn);
}
public override doubleacceptCash(double money)
{
double result = money;
if (money >= moneyCondition)
result = money -Math.Floor(money / moneyCondition) * moneyReturn;
return result;
}
}
class CashContext
{
CashSuper cs;
public CashContext(string type)
{
//简单工厂构造函数,克服客户端需要知道所以算法子类的缺点(封装了变化)
switch (type)
{
case "正常收费":
cs = new CashNormal();
break;
case "满300返100":
cs = newCashReturn("300", "100");
break;
case "打8折":
cs = newCashRebate("0.8");
break;
}
}
//和简单工厂的不同之处
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
CashContext csuper =new CashContext(cmbDiscount.SelectedItem.ToString());
double totalPrices = 0d;
totalPrices =csuper.GetResult(double.Parse(this.txtPrice.Text) *double.Parse(this.txtNumber.Text));
this.lbxResult.Items.Add("单价:" + this.txtPrice.Text + "数量:" + this.txtNumber.Text + " " + cmbDiscount.SelectedItem +" " + "合计" + totalPrices.ToString());
total = total + totalPrices;
this.lblAmount.Text =total.ToString();
总结
1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
2)在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。2)
3)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。
|