策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户。何谓算法呢?它是一种描述程序行为的语言,广泛应用于计算机科学领域,是一种让程序最为简洁的思考方式。
一、组成:
1.抽象策略角色: 策略类,通常由一个接口或者抽象类实现。
2.具体策略角色:包装了相关的算法和行为。
3.环境角色:持有一个策略类的引用,最终给客户端调用。
二、应用场景:
1、多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
三、优缺点:
1.优点:
恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。将每个算法都封装起来,并且使它们之间可以互换,客户端调用它们的时候互不影响。从而降低了系统的耦合度,提高了扩展性和可重用性,易于测试和维护。
2.缺点:
策略模式只适用于客户端知道所有的算法或行为的情况。策略模式造成很多的策略类,每个具体策略类都会产生一个新类,可以使用享元模式来减少对象的数量。
四、策略模式vs简单工厂模式:
工厂模式是创建型模式,关注对象的创建。策略模式是行为型模式,关注行为的封装。简单工厂模式是根据不同的条件返回一个适合的类给你使用,然后调用者使用工厂类返回的类去完成相应的操作。而策略模式是首先创建一个想使用的类实例,然后实例被当作参数传递进去,既而通过该实例去调用不用的算法。
五、代码示范:
某超市平时正常收费,周六日打八折,周年庆满300返100。下面我们利用已经学到的策略模式与简单工厂相结合编写一个收银程序。
1.抽象策略类:
//现金收取父类
abstract class CashSuper
{
//抽象方法:收取现金,参数为原价,返回为当前价
public abstract double acceptCash(double money);
}
2.具体策略角色:
//正常收费,继承CashSuper
class CashNormal : CashSuper
{
public override double acceptCash(double money)
{
return money;
}
}
//打折收费,继承CashSuper
class CashRebate : CashSuper
{
private double moneyRebate = 1d;
//初始化时,必需要输入折扣率,如八折,就是0.8
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double acceptCash(double money)
{
return money * moneyRebate;
}
}
//返利收费,继承CashSuper
class CashReturn : CashSuper
{
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
//初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
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;
}
}
3.环境角色:
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;
}
}
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
4.客户端:
//客户端窗体程序(主要部分)
double total = 0.0d;
private void btnOk_Click(object sender, EventArgs e)
{
//利用简单工厂模式根据下拉选择框,生成相应的对象
CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());
double totalPrices = 0d;
//通过多态,可以得到收取费用的结果
totalPrices = csuper.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
total = total + totalPrices;
lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " "
+ cbxType.SelectedItem + " 合计:" + totalPrices.ToString());
lblResult.Text = total.ToString();
}
5.窗体展示:
6.如果不使用策略模式,只用简单工厂实现,会怎样呢?请看客户端的代码。
//客户端窗体程序(主要部分)
double total = 0.0d;
private void btnOk_Click(object sender, EventArgs e)
{
//利用简单工厂模式根据下拉选择框,生成相应的对象
CashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString());
double totalPrices = 0d;
//通过多态,可以得到收取费用的结果
totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
total = total + totalPrices;
lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " "
+ cbxType.SelectedItem + " 合计:" + totalPrices.ToString());
lblResult.Text = total.ToString();
}
如果不使用策略模式,超市需要经常性地更改收费方式,每次维护和扩展都要去改动CashFactory,重新编译部署代码,这样就很麻烦了。策略模式定义了一系列算法(收费方式)的方法,这些算法完成的都是相同的工作,那就是GetResult。应用简单工厂把实例化具体策略类的过程由客户端转移到Context类中,这样客户端只需认识一个类CashContext,具体的收费算法彻底地与客户端分离,从而降低了耦合度,易于维护。
有了这样的的程序,收银员即使没学过数学也能胜任这份工作,只需在组合框中选择正确的收费方式,然后去扫码就可以了。超市打折时,不必去数据库中一个一个改动商品的价格,也不必耗费时间和劳动力去给商品重新贴价格了。
变是永远不变的,如果超市要打九折、满200返50呢,如何避免更改CashContext中的代码呢?这就要用到我们以后要学的反射技术了,办法总是比困难多的。一款好的软件虽然是看不见摸不着的,但它们发挥的作用却是不可估量的,所以好好学设计模式吧,设计出优秀的软件,为人民服务!