本文共 4135 字,大约阅读时间需要 13 分钟。
定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
例子:商场收银,营业员根据客户所购买商品的单价和数量,向客户收费,但有时候会有打折或者满减活动,eg:打八折,满300送100
先用上个学习到的简单工厂实现一下
建立一个现金收费的抽象类
package celv;abstract class CashSuper {// 现金收取超类的抽象方法,收取现金,参数为原价,返回伪当前价 public abstract double acceptCash(double money);}
正常收费子类
package celv;public class CashNormal extends CashSuper{ @Override public double acceptCash(double money) { // TODO Auto-generated method stub return money; }}
折扣收费子类
package celv;public class CashRebate extends CashSuper{ public double moneyRebate=1; public CashRebate(String moneyRebate){ this.moneyRebate=Double.parseDouble(moneyRebate); } @Override public double acceptCash(double money) { // TODO Auto-generated method stub return money*moneyRebate; }}
满减收费子类
package celv;public class CashReturn extends CashSuper{ public double moneyCondition=0.0; public double moneyReturn=0.0; public CashReturn(String moneyCondition,String moneyReturn){ this.moneyCondition=Double.parseDouble(moneyCondition); this.moneyReturn=Double.parseDouble(moneyReturn); } @Override public double acceptCash(double money) { // TODO Auto-generated method stub double result=money; if(money>=moneyCondition){ result=money-Math.floor(money/moneyCondition)*moneyReturn; } return result; }}
一个工厂类来生成不同实例
package celv;public class CashFactory { public static CashSuper createCashAccept(String type){ CashSuper cs=null; switch(type){ case "收费正常": CashNormal cs0=new CashNormal(); cs=cs0; break; case "满300返100": CashReturn cr1=new CashReturn("300","100"); cs=cr1; break; case "打八折": CashRebate cr2=new CashRebate("0.8"); cs=cr2; break; } return cs; }}
测试
package celv;public class Test { public static void main(String[] args) { // TODO Auto-generated method stub double price=50; double number=20; double total=0; String type="满300返100";// 简单工厂模式 CashSuper csuper=CashFactory.createCashAccept(type); total=csuper.acceptCash(price*number); System.out.println(total); }}
上述可以完成基本功能要求,但是,工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以至于代码需要重新编译部署。
策略模式:商场收银时如何促销,打折还是返利,其实都是一些算法,用工厂来生成算法对象没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,封装变化点是面向对象的一种很重要的思维方式。
于是,可以不用工厂,建立一个Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的应用,即:
package celv;public class CashContext { public CashSuper cs;// 通过构造方法,传入具体的收费策略 public CashContext(CashSuper cs){ this.cs=cs; } // 根据收费策略的不同,获得计算结果 public double getResult(double money){ return cs.acceptCash(money); } }
此时,客户端的测试可以写为:
package celv;public class celvTest { public static void main(String[] args) { // TODO Auto-generated method stub double price=50; double number=20; double total=0; String type="满300返100"; CashContext cc=null; switch(type){ case "收费正常": cc=new CashContext(new CashNormal()); break; case "满300返100": cc=new CashContext(new CashReturn("300","100")); break; case "打八折": cc=new CashContext(new CashRebate("0.8")); break; } total=cc.getResult(price*number); System.out.println(total); }}
但是也存在缺点,即让客户端去判断用哪一个算法,可以试着将简单工工厂和策略模式进行结合,将CashContext类修改
package celv;public class CashContext { public CashSuper cs;// 通过构造方法,传入具体的收费策略 public CashContext(CashSuper cs){ this.cs=cs; } 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 "打八折": CashRebate cr2=new CashRebate("0.8"); cs=cr2; break; } }// 根据收费策略的不同,获得计算结果 public double getResult(double money){ return cs.acceptCash(money); } }
那么,客户端代码就是这样子
package celv;public class Test { public static void main(String[] args) { // TODO Auto-generated method stub double price=50; double number=20; double total=0; String type="满300返100"; // 策略模式与简单工厂结合 CashContext csuper=new CashContext(type); total=csuper.getResult(price*number); System.out.println(total); }}
由此可以看出,简单工厂模式让客户端认识两个类,即CashSuper和CashFactory,而策略模式与简单工厂结合,客户端只需要认识一个类CashContext,耦合度更低。
总结:策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
参考自《大话设计模式》,代码自己有所修改,持续更新。
转载地址:https://blog.csdn.net/Autumn03/article/details/80740193 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!