本文转载自点击打开链接
需求
实现一份金庸知识考试的试卷。
实现
级别1
public class Cash { private double total = 0; public void submit(int num, double price) { double totalPrices = num * price; total += totalPrices; System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices); } public double getTotal() { return total; } public void setTotal(double total) { this.total = total; } }级别2
增加打折的功能。
public class Cash { private double total = 0; private int selectedIndex = 0; public void selectFormLoad() { String[] selectForm = { "正常收费", "打8折", "打7折", "打5折" }; selectedIndex = 0; } public void submit(int num, double price) { double totalPrices = 0; switch (selectedIndex) { case 0: totalPrices = num * price; break; case 1: totalPrices = num * price * 0.8; break; case 2: totalPrices = num * price * 0.7; break; case 3: totalPrices = num * price * 0.5; break; } total += totalPrices; System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices); } public double getTotal() { return total; } public void setTotal(double total) { this.total = total; } public int getSelectedIndex() { return selectedIndex; } public void setSelectedIndex(int selectedIndex) { this.selectedIndex = selectedIndex; } }
级别3
面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同的属性和功能的对象的抽象集合才是类。打1折和打9折只是形式不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。我们来试着用上次讲过的简单工厂模式实现。
//现金收费接口 public interface CashSuper { public double acceptCash(double money); }
//正常收费子类 public class CashNormal implements CashSuper { public double acceptCash(double money) { return money; } }
//打折收费子类 public class CashRebate implements CashSuper { private double moneyRebate = 1; public CashRebate(double moneyRebate) { this.moneyRebate = moneyRebate; } public double acceptCash(double money) { return money * moneyRebate; } }
//返利收费子类 public class CashReturn implements CashSuper { private double moneyCondition = 0; private double moneyReturn = 0; public CashReturn(double moneyCondition, double moneyReturn) { this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } public double acceptCash(double money) { double result = money; if (money >= moneyCondition) { int temp=(int)(money/moneyCondition); result = money -temp* moneyReturn; } return result; } }
//现金收费工厂类 public class CashFactory { public static CashSuper createCash(String type) { CashSuper cs = null; if ("正常收费".equals(type)) { cs = new CashNormal(); } else if ("满300返100".equals(type)) { cs = new CashReturn(300, 100); } else if ("打8折".equals(type)) { cs = new CashRebate(0.8); } return cs; } }
//客户端代码 public class Main { private static double total = 0; public static void main(String[] args) { consume("正常收费", 1, 1000); consume("满300返100", 1, 1000); consume("打8折", 1, 1000); System.out.println("总计:" + total); } public static void consume(String type, int num, double price) { CashSuper csuper = CashFactory.createCash(type); double totalPrices = 0; totalPrices = csuper.acceptCash(num * price); total += totalPrices; System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices); } }简单工厂模式虽然也能解决这个问题,但是这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度的,每次维护或扩展收费方式都要改动这个工厂,以致代码需要重新编译部署,这是非常糟糕的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。
级别4
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
商场收银时,如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,而封装变化点是我们面向对象的一种很重要的思维方式。
策略模式的基本代码:
//Strategy类,定义所有支持的算法的公共接口 public interface Strategy { public void algorithmInterface(); } //ConcreteStrategy封装了具体的算法或行为,继承于Strategy public class ConcreteStrategyA implements Strategy { public void algorithmInterface() { System.out.println("算法A实现"); } } public class ConcreteStrategyB implements Strategy { public void algorithmInterface() { System.out.println("算法A实现"); } } public class ConcreteStrategyC implements Strategy { public void algorithmInterface() { System.out.println("算法C实现"); } } //Context用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用 public class Context { private Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public void contextInterface() { strategy.algorithmInterface(); } } //客户端代码 public class Main { public static void main(String[] args) { Context context; context = new Context(new ConcreteStrategyA()); context.contextInterface(); context = new Context(new ConcreteStrategyB()); context.contextInterface(); context = new Context(new ConcreteStrategyC()); context.contextInterface(); } }加一个CashContext类,去掉CashFactory类,并改一下客户端就可以了。
//CashContext类 public class CashContext { CashSuper cashSuper; public CashContext(CashSuper cashSuper) { this.cashSuper = cashSuper; } public double acceptCash(double money) { return cashSuper.acceptCash(money); } }
//客户端代码 public class Main { private static double total = 0; public static void main(String[] args) { consume("正常收费", 1, 1000); consume("满300返100", 1, 1000); consume("打8折", 1, 1000); System.out.println("总计:" + total); } public static void consume(String type, int num, double price) { CashContext cashContext = null; if ("正常收费".equals(type)) { cashContext = new CashContext(new CashNormal()); } else if ("满300返100".equals(type)) { cashContext = new CashContext(new CashReturn(300, 100)); } else if ("打8折".equals(type)) { cashContext = new CashContext(new CashRebate(0.8)); } double totalPrices = cashContext.acceptCash(num * price); total += totalPrices; System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices); } }
级别5
试着将策略模式和简单工厂模式相结合。
//改造后的CashContext public class CashContext { CashSuper cashSuper; public CashContext(CashSuper cashSuper) { this.cashSuper = cashSuper; } public CashContext(String type) { if ("正常收费".equals(type)) { cashSuper = new CashNormal(); } else if ("满300返100".equals(type)) { cashSuper = new CashReturn(300, 100); } else if ("打8折".equals(type)) { cashSuper = new CashRebate(0.8); } } public double acceptCash(double money) { return cashSuper.acceptCash(money); } }
//客户端代码 public class Main { private static double total = 0; public static void main(String[] args) { consume("正常收费", 1, 1000); consume("满300返100", 1, 1000); consume("打8折", 1, 1000); System.out.println("总计:" + total); } public static void consume(String type, int num, double price) { CashContext cashContext = new CashContext(type); double totalPrices = cashContext.acceptCash(num * price); total += totalPrices; System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices); } }
简单工厂模式需要让客户端认识两个类,CashSuper和CashFactory,而策略模式与简单工厂模式结合的用法,客户端就只需要认识一个类CashContext。耦合度更加降低。我们在客户端实例化的是CashContext的对象,调用的是CashContext的方法,这使得具体的收费算法彻底地与客户端分离。连算法的父类CashSuper都不让客户端认识了。相当于创建了一个句柄类。看到这里可能会有点糊涂了,策略模式和简单工厂模式有什么区别呢?我们举一个简单的例子:
工厂模式:有一天你决定去吃披萨,一看菜单,哦,种类很多呀,你就点了个培根披萨,过了二十分钟,你的披萨就来了就可以吃到了。但这个披萨是怎么做的,到底面粉放了多少,培根放了多少,佐料放了多少,有多少到工序,你是不需要管的,你需要的是一个美味培根披萨。
策略模式:同样还是在披萨店,你要一个培根披萨,老板说想吃自己去做吧。原料有培根、面粉、佐料。工序有1、2、3工序,你自己去做吧。然后你就需要自己去做,到底放多少培根,放多少面粉,放多少佐料,这都你自己来决定,工序1、2、3,你是怎么实现的,都你自己决定。最后你得到了披萨。
总结一下:工厂类的使用者需要不是自己创建的对象,策略模式中Context类的使用者需要的是自己创建的对象。