1.1 商场收银软件
package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); double price = 0d; //商品单价int num = 0; //商品购买数量double totalPrices = 0d;//当前商品合计费用double total = 0d; //总计所有商品费用Scanner sc = new Scanner(System.in);do {System.out.println("请输入商品单价:"); price = Double.parseDouble(sc.nextLine());System.out.println("请输入商品数量:"); num = Integer.parseInt(sc.nextLine());System.out.println(); if (price>0 && num>0){totalPrices = price * num;total = total + totalPrices;System.out.println(); System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元"); System.out.println();System.out.println("总计:"+ total+"元"); System.out.println();}}while(price>0 && num>0);System.out.println(); System.out.println("**********************************************");}
}
1.2 增加打折
1.正常收费 2.打八折 3.打七折
package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); int discount = 0; //商品折扣模式(1.正常收费 2.打八折 3.打七折)double price = 0d; //商品单价int num = 0; //商品购买数量double totalPrices = 0d;//当前商品合计费用double total = 0d; //总计所有商品费用Scanner sc = new Scanner(System.in);do {System.out.println("请输入商品折扣模式(1.正常收费 2.打八折 3.打七折):"); discount = Integer.parseInt(sc.nextLine());System.out.println("请输入商品单价:"); price = Double.parseDouble(sc.nextLine());System.out.println("请输入商品数量:"); num = Integer.parseInt(sc.nextLine());System.out.println(); if (price>0 && num>0){switch(discount){case 1:totalPrices = price * num;break;case 2:totalPrices = price * num * 0.8;break;case 3:totalPrices = price * num * 0.7;break;}total = total + totalPrices;System.out.println(); System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元"); System.out.println();System.out.println("总计:"+ total+"元"); System.out.println();}}while(price>0 && num>0);System.out.println(); System.out.println("**********************************************");}
}
1.3 简单工厂实现
商场的活动加大,需要有满300返回100的促销算法,又增加是700就要返200呢?或者说需要现在打三折,又要满300送80呢?
收费抽象类
package com.lhx.design.pattern.test;
//收费抽象类
public abstract class CashSuper {//收取费用的抽象方法,参数为单价和数量public abstract double acceptCash(double price,int num);}
package com.lhx.design.pattern.test;public class CashNormal extends CashSuper {//正常收费,原价返回public double acceptCash(double price,int num){return price * num;}}
package com.lhx.design.pattern.test;public class CashRebate extends CashSuper {private double moneyRebate = 1d;//打折收费。初始化时必需输入折扣率。八折就输入0.8public CashRebate(double moneyRebate){this.moneyRebate = moneyRebate;}//计算收费时需要在原价基础上乘以折扣率public double acceptCash(double price,int num){return price * num * this.moneyRebate;}}
package com.lhx.design.pattern.test;public class CashReturn extends CashSuper {private double moneyCondition = 0d; //返利条件private double moneyReturn = 0d; //返利值//返利收费。初始化时需要输入返利条件和返利值。//比如“满300返100”,就是moneyCondition=300,moneyReturn=100public CashReturn(double moneyCondition,double moneyReturn){this.moneyCondition = moneyCondition;this.moneyReturn = moneyReturn;}//计算收费时,当达到返利条件,就原价减去返利值public double acceptCash(double price,int num){double result = price * num;if (moneyCondition>0 && result >= moneyCondition)result = result - Math.floor(result / moneyCondition) * moneyReturn; return result;}}
package com.lhx.design.pattern.test;public class CashFactory {//收费工厂public static CashSuper createCashAccept(int cashType){CashSuper cs = null;switch (cashType) {case 1:cs = new CashNormal(); //正常收费break;case 2:cs = new CashRebate(0.8d); //打八折break;case 3:cs = new CashRebate(0.7d); //打七折break;case 4:cs = new CashReturn(300d,100d);//满300返100break;}return cs;}}
package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); int discount = 0; //商品折扣模式(1.正常收费 2.打八折 3.打七折)double price = 0d; //商品单价int num = 0; //商品购买数量double totalPrices = 0d;//当前商品合计费用double total = 0d; //总计所有商品费用Scanner sc = new Scanner(System.in);do {System.out.println("请输入商品折扣模式(1.正常收费 2.打八折 3.打七折 4.满300送100):"); discount = Integer.parseInt(sc.nextLine());System.out.println("请输入商品单价:"); price = Double.parseDouble(sc.nextLine());System.out.println("请输入商品数量:"); num = Integer.parseInt(sc.nextLine());System.out.println(); if (price>0 && num>0){//简单工厂模式根据discount的数字选择合适的收费类生成实例CashSuper csuper = CashFactory.createCashAccept(discount);//通过多态,可以根据不同收费策略计算得到收费的结果totalPrices = csuper.acceptCash(price,num);total = total + totalPrices;System.out.println(); System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元"); System.out.println();System.out.println("总计:"+ total+"元"); System.out.println();}}while(price>0 && num>0);System.out.println();System.out.println("**********************************************");}
}
1.4 策略模式
我要是需要打五折和满500送200的促销活动,只要在现金工厂当中加两个条件,就okl ,准确的来说是收费对象生产工厂才正确,如果我现在需要增加一种商场促销手段,满100积分10点,以后到一定时候可以领取奖品如何做?
"有了工厂,何难?加一个积分算法,构造方法有两个参数:条件和返点,让它继承CashSuper,再到现金工厂,哦,不对,是收费对象生成工厂里增加满100积分10点的分支条件,再到界面稍加改动,就行了。"
简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,这真的是很糟糕的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。好好去研究一下其他的设计模式,你会找到答案的。
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
Strategy类,定义所有支持的算法的公共接口:
Context类,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
客户端代码:
1.5 策略模式实现
package com.lhx.design.pattern.test;public class CashContext {private CashSuper cs; //声明一个CashSuper对象//通过构造方法,传入具体的收费策略public CashContext(CashSuper csuper){this.cs = csuper;}public double getResult(double price,int num){//根据收费策略的不同,获得计算结果return this.cs.acceptCash(price,num);}
}
package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); int discount = 0; //商品折扣模式(1.正常收费 2.打八折 3.打七折)double price = 0d; //商品单价int num = 0; //商品购买数量double totalPrices = 0d;//当前商品合计费用double total = 0d; //总计所有商品费用Scanner sc = new Scanner(System.in);do {System.out.println("请输入商品折扣模式(1.正常收费 2.打八折 3.打七折 4.满300送100):"); discount = Integer.parseInt(sc.nextLine());System.out.println("请输入商品单价:"); price = Double.parseDouble(sc.nextLine());System.out.println("请输入商品数量:"); num = Integer.parseInt(sc.nextLine());System.out.println(); if (price>0 && num>0){CashContext cc = null;//根据用户输入,将对应的策略对象作为参数传入CashContent对象中switch(discount){case 1:cc = new CashContext(new CashNormal());break;case 2:cc = new CashContext(new CashRebate(0.8d));break;case 3:cc = new CashContext(new CashRebate(0.7d));break;case 4:cc = new CashContext(new CashReturn(300d,100d));break;}//通过Context的getResult方法的调用,可以得到收取费用的结果//让具体算法与客户进行了隔离totalPrices = cc.getResult(price,num);total = total + totalPrices;System.out.println(); System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元"); System.out.println();System.out.println("总计:"+ total+"元"); System.out.println();}}while(price>0 && num>0);System.out.println();System.out.println("**********************************************");}
}
把这个判断过程从客户端程序移走
1.6 策略与简单工厂结合
package com.lhx.design.pattern.test;public class CashContext {private CashSuper cs; //声明一个CashSuper对象//通过构造方法,传入具体的收费策略public CashContext(int cashType){switch(cashType){case 1:this.cs = new CashNormal();break;case 2:this.cs = new CashRebate(0.8d);break;case 3:this.cs = new CashRebate(0.7d);break;case 4:this.cs = new CashReturn(300d,100d);break;}}public double getResult(double price,int num){//根据收费策略的不同,获得计算结果return this.cs.acceptCash(price,num);}
}
package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); int discount = 0; //商品折扣模式(1.正常收费 2.打八折 3.打七折)double price = 0d; //商品单价int num = 0; //商品购买数量double totalPrices = 0d;//当前商品合计费用double total = 0d; //总计所有商品费用Scanner sc = new Scanner(System.in);do {System.out.println("请输入商品折扣模式(1.正常收费 2.打八折 3.打七折 4.满300送100):"); discount = Integer.parseInt(sc.nextLine());System.out.println("请输入商品单价:"); price = Double.parseDouble(sc.nextLine());System.out.println("请输入商品数量:"); num = Integer.parseInt(sc.nextLine());System.out.println(); if (price>0 && num>0){//根据用户输入,将对应的策略对象作为参数传入CashContext对象中CashContext cc = new CashContext(discount);//通过Context的getResult方法的调用,可以得到收取费用的结果//让具体算法与客户进行了隔离totalPrices = cc.getResult(price,num);total = total + totalPrices;System.out.println(); System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元"); System.out.println();System.out.println("总计:"+ total+"元"); System.out.println();}}while(price>0 && num>0);System.out.println();System.out.println("**********************************************");}
}
简单工厂模式我需要客户端认识两个类,CashSuper和CashFacory,而策略模式与简单工厂结合的用法,客户端就只需要认识一个类CashContext就可以了。耦合更加降低。
说得没错,我们在客户端实例化的是CashContext的对象,调用的是CashContext的方GetResult,这使得具体的收费算法彻底地与客户端分离。连算法的父类CashSuper都不让客户端认识了。
1.7 策略模式解析
策略模式封装了变化。还有什么不足吗?
因为在CashContext里还是用到了switch,也就是说,如果我们需要增加一种算法,比如'满200送50',你就必须要更改CashContext中的switch代码,这总还是让人很不爽呀。那你说怎么办,有需求就得改呀,任何需求的变更都是需要成本的,但是成本的高低还是有差异的。高手和菜鸟的区别就是高手可以花同样的代价获得最大的收益或者说做同样的事花最小的代价。面对同样的需求,然是改动越小越好。""你的意思是说,还有更好的办法?""当然。这个办法就是用到了反射Reflect)技术,不过今天就不讲了,以后会再提它的。""反射真有这么神奇?"小菜疑惑地望向了远方。
注:在抽象工厂模式章节有对反射的讲解。