首先解释下,我每次贴出来的代码。都是只给出主要部分的代码。便于大家理解,有人说运行不了的。请谅解哈。
上次我们简单的介绍了简单工厂模式,我们知道,只要有统一归类的操作,我们就可以抽象出一个父类出来,然后根据不同的条件来用工厂模式实例化不同的对象,然后用父类的姿态显示出来。但是相对于我们面向对象的编程思想来说。这种简单工厂模式还是有一定的缺点的,一旦我们需要再添加新的操作,就需要再定义一个新的子类并且继承同一个父类,而且需要修改工厂类中的代码,因为我们要添加新的子类和判断条件(比如我需要给计算器增加一个开平方的功能),在Operation类还需要增加相应的判断条件。很明显,这种模式的有着高耦合,一旦我们需要改变需求,就需要大量修改修改原本已经写好的代码,在需求的不断变化过程中,这不是一件很好的事。
现在呢。在我们面前会再有一个案例。一个商场打算做一种促销,促销方式有两种。
1.购物满500返200.
2.超过200元打8折。
然后计算出客户购买商品之后应付的金额。
这样的需求在不变得情况下是很容易写出来的。但是,如果我需要把打折的数字和返现的数字是需要变化的呢?或者我们需要再添加一种新的打折方式呢?
虽然简单工厂模式可以实现这个功能,但是商场的这种促销模式是不断的改变的,所以简单工厂的代码就需要不断的修改,程序就需要不断的再次编译和生产。那么好的方法是什么呢?
面对算法的经常变动,策略模式就应运而生。
面向对象的编程,并不是类越多就越好,类的划分是为了封装,但是分类的基础和原则是抽象,具有相同属性和功能的对象的抽象集合才是类。
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,该模式让算法的变化,不会影响到使用算法的客户。
首先。定义算法抽象类,算法抽象类中有个算法实现的抽象方法,各种算法继承算法抽象类,然后重写算法抽象方法
其次,定义Context类。Context为上下文类,在该类中先声明一个抽象类的对象,然后按照客户所选的条件实例化具体的算法对象。然后再在该类中写一个方法,这个方法中其实是调用了抽象类的方法,用来显示算法对象的功能。
最后我们只需要定义不同的算法来继承算法抽象类即可。
下面给大家看看策略模式的图解:
关于上面我们解释的策略模式,我们就用收银这个案例来实现下。代码虽然是我敲到博客中来的,但是该案例出自大话设计模式中。
namespace 收银台 { /// <summary> /// 现金收费抽象类 /// </summary> abstract class CashSuper { public abstract double acceptCash(double money); } /// <summary> /// 正常收费继承CashSuper /// </summary> class CashNormal : CashSuper { public override double acceptCash(double money) { return money; } } /// <summary> /// 打折收费类 /// </summary> class CashRebate : CashSuper { private double moneyRebate = 1d; public CashRebate(string moneyRebate) { //在构造方法中对打折进行赋值 this.moneyRebate = double.Parse(moneyRebate); } public override double acceptCash(double money) { return money*moneyRebate; } } /// <summary> /// 现金返利类 /// </summary> class CashReturn : CashSuper { private double moneyCondition = 0.0d;//返利起点 private double moneyReturn = 0.0d;//返利金额 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; } } /// <summary> /// 现金收费工厂类 /// 有了策略模式 就不在需要这个简单工厂来产生需要的对象。 /// </summary> class CashFactory { public static CashSuper CreatCashAccept(string type) { CashSuper cs = null; switch (type) { case "正常收费": cs=new CashNormal(); break; case "满300反100": cs = new CashReturn("300","100"); break; case "打8折": cs = new CashRebate("0.8"); break; } return cs; } } /// <summary> /// 上下文类 /// </summary> class Context { private CashSuper cs=null; public Context(string type) { //修改了Context 把实例化子类 直接放在了上下文的构造方法中 //参数是有UI界面得来的 彻底分离了UI与逻辑。 switch (type) { case "正常收费": cs = new CashNormal(); break; case "满300反100": cs = new CashReturn("300", "100"); break; case "打8折": cs = new CashRebate("0.8"); break; } } public double Getresult(double money) { return cs.acceptCash(money); } } }
然后,我们再给出客户端的主要代码。
private void btnok_Click(object sender, EventArgs e) { /*在这里使用策略模式 一样是创建上下文类来控制到底使用哪种算法 需要用上下文类来实现算法的选择以及把实例化的子类赋值给 * 上下文中的父类。 * 再用上下文类中的方法来调用父类的从子类中获得到的方法。 */ Context ct = new Context(cmb1.SelectedItem.ToString()); double totalPrice = 0d; totalPrice = ct.Getresult(Convert.ToDouble(txtboxprice.Text) * Convert.ToDouble(txtboxnumber.Text));//计算每个商品的总价 total = total + totalPrice; listBox1.Items.Add("单价:" + txtboxprice.Text + "数量" + txtboxnumber.Text + " " + cmb1.SelectedText + "合计" + totalPrice.ToString()); labCount.Text = total.ToString(); }
我们从客户端的代码中不难发现,我们把进行判断的事情都放在了上下文Context类中。这个类不但维护了判断条件,而且所有算法对象的实现都是在这个类中产生。一旦需要修改,我们需要做的就是添加算法类继承抽象算法,和在Context类中增加对该算法的判断即可。
虽然这个策略模式在需求变化的同时,并没有完全的解耦,还是需要修改程序代码,但是相比简单工厂的需要修改两处,这里需要修改一处。是不是已经好了很多呢?
我们拿出来计算器-简单工厂的客户端代码。
class Program { static void Main(string[] args) { Console.WriteLine("请输入第一个数:"); int num1 = int.Parse(Console.ReadLine()); Console.WriteLine("请输入第二个数:"); int num2 = int.Parse(Console.ReadLine()); Console.WriteLine("请输入运算符:"); string mark = Console.ReadLine(); Operation opc = FactoryClass.Panduan(mark); if (opc != null) { Console.WriteLine(opc.Run(num1, num2)); } else { Console.WriteLine("无效的运算符!"); } Console.ReadKey(); } }
在这里,我们的客户端需要认识两个类才能完成计算的操作,Operation,和Factory。
但是我们回头看看策略模式,我们的客户端只需要认识一个Context上下文类,就能完全实现这些功能,耦合度也就随之降低了。
策略模式和简单工厂的配合使用
策略模式是一种定义一系列算法的方法,从概念上看,所有的这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各类算法与算法类之间的耦合性。策略模式也简化了单元测试,每个算法都有自己的类,可以通过直接的接口进行单独的测试。策略模式就是用来封装算法的,但是在实践中,几乎所有的类型规则,只要在分析过程中,听到在 不同的时间应用不同的业务规则。就可以考虑使用策略模式处理这种变化的可能性。