设计模式之策略模式
做一个商场收银软件。根据商场的特点,有时候打9折,有时候打3折;有时候满300减100,有时候满500减100;有时候消费100积10分,够100积分换什么等等,有着很多复杂的销售方式,而且特点是可能经常性的更改打折额度和返利额度。
首先,用简单工厂来实现下:
1 //现金收费抽象类 2 abstract class SaleOperation 3 { 4 //参数为原价,返回值为实际成交价格 5 public abstract double LastPrice(double money); 6 } 7 8 //正常收费 9 class SaleOperationNormal : SaleOperation 10 { 11 public override double LastPrice(double money) 12 { 13 return money; 14 } 15 } 16 17 //打折 18 class SaleOperationByCutOff:SaleOperation 19 { 20 //注意:1d表示双精度浮点数 21 private double _cutOffRadio = 1d; 22 //折扣,需要传入具体是3折还是9折 23 public SaleOperationByCutOff(double cutOffRadio) 24 { 25 this._cutOffRadio = cutOffRadio; 26 } 27 public override double LastPrice(double money) 28 { 29 return money * _cutOffRadio; 30 } 31 } 32 //返利收费 33 class SaleOperationByEnoughSend : SaleOperation 34 { 35 private double _moneyCondition = 0.0d; 36 private double _moneyReturn = 0.0d; 37 //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100 38 public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn) 39 { 40 this._moneyCondition = moneyCondition; 41 this._moneyReturn = moneyReturn; 42 } 43 public override double LastPrice(double money) 44 { 45 double result = money; 46 //符合返利条件,则实际收取的钱要减去返利值 47 if (money > _moneyCondition) 48 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn; 49 return result; 50 } 51 }
1 class SaleOperationFactroy 2 { 3 //type 为促销方式选择,1为打折,2为返利等等 4 public static SaleOperation createSaleOperation(int type) 5 { 6 SaleOperation so = null; 7 switch (type) 8 { 9 case 1: 10 so = new SaleOperationByCutOff(0.8);//必须在这里确定是八折,如果换成了7折,需要修改代码 11 break; 12 case 2: 13 so = new SaleOperationByEnoughSend(300,100);//必须在这里确定是300返100,如果是500返100,需要修改代码 14 break; 15 } 16 return so; 17 } 18 }
1 double totalPrice = 0d; 2 public void btnOK() 3 { 4 double thisPrice = 0d; 5 SaleOperation so = SaleOperationFactroy.createSaleOperation(1); 6 //根据多态,取得本次商品实际价格(单品价格*数量) 7 thisPrice = so.LastPrice(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text); 8 //计入总价 9 totalPrice = totalPrice + thisPrice; 10 }
由此可见简单工厂的弊端:商场经常性的变更折扣额度和返利额度的时候,每次都要对工厂类进行修改,重新编译源码,重新编译部署,不能应对算法的时常变动。
由此我们使用策略模式(Strategy):定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式是定义了一系列算法的方法,从概念上来看,所有的算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有算法,减少了各种算法类与使用算法类之间的耦合。另外采取继承有助于析取出这些算法的公共功能。
商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂生成算法本身没错,但是算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这是关键变化点,而封装变化点是面向对象一种很重要的思维方式。
策略模式:
1 //现金收费抽象类 2 abstract class SaleOperation 3 { 4 //参数为原价,返回值为实际成交价格 5 public abstract double LastPrice(double money); 6 } 7 8 //正常收费 9 class SaleOperationNormal : SaleOperation 10 { 11 public override double LastPrice(double money) 12 { 13 return money; 14 } 15 } 16 17 //打折 18 class SaleOperationByCutOff:SaleOperation 19 { 20 //注意:1d表示双精度浮点数 21 private double _cutOffRadio = 1d; 22 //折扣,需要传入具体是3折还是9折 23 public SaleOperationByCutOff(double cutOffRadio) 24 { 25 this._cutOffRadio = cutOffRadio; 26 } 27 public override double LastPrice(double money) 28 { 29 return money * _cutOffRadio; 30 } 31 } 32 //返利收费 33 class SaleOperationByEnoughSend : SaleOperation 34 { 35 private double _moneyCondition = 0.0d; 36 private double _moneyReturn = 0.0d; 37 //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100 38 public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn) 39 { 40 this._moneyCondition = moneyCondition; 41 this._moneyReturn = moneyReturn; 42 } 43 public override double LastPrice(double money) 44 { 45 double result = money; 46 //符合返利条件,则实际收取的钱要减去返利值 47 if (money > _moneyCondition) 48 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn; 49 return result; 50 } 51 } 52 53 //****核心***** 定义一个上下文类 54 //Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类, 55 //而是将生成类所实现的功能接口包装一次,提供给客户。 56 //这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。 57 class SaleContext 58 { 59 //生命一个现金收费抽象类对象 60 SaleOperation _sc; 61 //通过构造方法,传入具体的收费策略 62 public SaleContext(SaleOperation sc) 63 { 64 this._sc = sc; 65 } 66 //根据传入的具体对象,得到不同的最终价格 67 public double GetResult(double money) 68 { 69 return _sc.LastPrice(money); 70 } 71 }
1 public void btnOK() 2 { 3 //定义一个上下文类 4 SaleContext sc = null; 5 switch (type) 6 { 7 case 1: 8 sc = new SaleContext(new SaleOperationByCutOff(0.8)); 9 break; 10 case 2: 11 sc = new SaleContext(new SaleOperationByEnoughSend(300,100)); 12 break; 13 14 } 15 double thisPrice = 0d; 16 SaleOperation so = SaleOperationFactroy.createSaleOperation(1); 17 //根据多态,对上下文类调用其方法。取得最终收取费用结果,让具体算法和客户进行了隔离 18 thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text); 19 //计入总价 20 totalPrice = totalPrice + thisPrice; 21 }
策略模式和工厂模式类似,还是负责根据输入参数的不同来生成不同的子类,并将生成的子类作为基类返回(这样可以让客户端在使用的时候很方便),不过把选择的地方放在了客户端。客户端只需要调用上下文类创建一个基类的实例,然后调用这个实例的函数来实现自己的功能即可。
策略模式比起工厂模式来更强大,它可以在运行中对这个属性进行改变,而工厂模式中使用的是一个对象,对象就没法在运行中改变了,没办法在运行中随时变成其它分支类。这一点上来说,策略模式应该是比工厂模式更灵活的一个加强版。
策略模式使用时必须在客户端首先创建一个想使用的类对象,然后将该对象最为参数传递进去,通过该对象调用不同的算法,因为策略模式中并没有为我们实现具体实例的选取,只是从抽象类中调用了统一接口。在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。
策略模式就是用来封装算法的,在实践中,发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
和工厂模式的区别是,在工厂模式中,这个变量是个对象,而在策略模式中,这个变量成了对象的一个属性。另外,策略模式耦合度更低。
工厂模式客户端:需要认识两个类SaleOperation和SaleOperationFactroy
SaleOperation so = SaleOperationFactroy.createSaleOperation(1);
thisPrice = so.LastPrice(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);
策略模式+工厂模式客户端:只需要一个类SaleContext
SaleContext sc = new SaleContext(1);
thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);
策略模式具体适用于:
1.以不同的格式保存文件;
2.以不同的算法压缩文件;
3.以不同的算法截获图象;
4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等。
策略模式+简单工厂:仍然没有解决SWITCH问题。如果需要解决,必须用到反射。
1 public void btnOK() 2 { 3 //定义出上下文类的对象。 4 SaleContext sc = new SaleContext(1); 5 double thisPrice = 0d; 6 7 //根据多态,对上下文类调用其方法。取得最终收取费用结果,让具体算法和客户进行了隔离 8 thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text); 9 //计入总价 10 totalPrice = totalPrice + thisPrice; 11 }
1 //现金收费抽象类 2 abstract class SaleOperation 3 { 4 //参数为原价,返回值为实际成交价格 5 public abstract double LastPrice(double money); 6 } 7 8 //正常收费 9 class SaleOperationNormal : SaleOperation 10 { 11 public override double LastPrice(double money) 12 { 13 return money; 14 } 15 } 16 17 //打折 18 class SaleOperationByCutOff:SaleOperation 19 { 20 //注意:1d表示双精度浮点数 21 private double _cutOffRadio = 1d; 22 //折扣,需要传入具体是3折还是9折 23 public SaleOperationByCutOff(double cutOffRadio) 24 { 25 this._cutOffRadio = cutOffRadio; 26 } 27 public override double LastPrice(double money) 28 { 29 return money * _cutOffRadio; 30 } 31 } 32 //返利收费 33 class SaleOperationByEnoughSend : SaleOperation 34 { 35 private double _moneyCondition = 0.0d; 36 private double _moneyReturn = 0.0d; 37 //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100 38 public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn) 39 { 40 this._moneyCondition = moneyCondition; 41 this._moneyReturn = moneyReturn; 42 } 43 public override double LastPrice(double money) 44 { 45 double result = money; 46 //符合返利条件,则实际收取的钱要减去返利值 47 if (money > _moneyCondition) 48 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn; 49 return result; 50 } 51 } 52 53 //****核心***** 定义一个上下文类 54 //Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类, 55 //而是将生成类所实现的功能接口包装一次,提供给客户。 56 //这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。 57 class SaleContext 58 { 59 //声明一个现金收费抽象类对象 60 SaleOperation _sc; 61 62 public SaleContext(int type) 63 { 64 switch (type) 65 { 66 case 1: 67 SaleOperationByCutOff sbc = new SaleOperationByCutOff(0.8); //根据TYPE定义出返利具体信息,然后利用多态将对象定义回父类 68 _sc = sbc; 69 break; 70 case 2: 71 SaleOperationByEnoughSend sbes = new SaleOperationByEnoughSend(300,100); 72 _sc = sbes; 73 break; 74 } 75 } 76 //根据传入的具体对象,得到不同的最终价格 77 public double GetResult(double money) 78 { 79 return _sc.LastPrice(money); 80 } 81 }
浙公网安备 33010602011771号