商場收銀軟件,營業員根據客戶所購買的商品單價和數量,向客戶收費。算法
/** * 普通實現 * Created by callmeDevil on 2019/6/1. */ public class NormalTest { public static void main(String[] args) { double price = 10; double num = 5; System.out.println(String.format("單價:%s 元", price)); System.out.println(String.format("數量:%s 個", num)); System.out.println(String.format("總價:%s 元", calculateTotal(price, num))); } /** * 計算總價 * * @param price 單價 * @param num 數量 * @return */ private static double calculateTotal(double price, double num) { return price * num; } }
商品搞促銷,打八折,也可能打七折,甚至五折。編程
/** * 普通實現2 * Created by callmeDevil on 2019/6/1. */ public class NormalTest2 { public static void main(String[] args) { double price = 10; double num = 5; String[] discounts = {"正常收費", "打八折", "打七折", "打五折"}; System.out.println(String.format("單價:%s 元", price)); System.out.println(String.format("數量:%s 個", num)); System.out.println(String.format("折扣:%s ", discounts[1])); System.out.println(String.format("總價:%s 元", calculateTotal(price, num, 1))); } /** * 計算總價 * * @param price 單價 * @param num 數量 * @param discount 折扣 * @return */ private static double calculateTotal(double price, double num, int discount) { double total = 0L; switch (discount) { case 0: total = price * num; break; case 1: total = price * num * 0.8; break; case 2: total = price * num * 0.7; break; case 3: total = price * num * 0.5; break; default: break; } return total; } }
有不少重複代碼,就switch語句來講,若是計算方式比較複雜,那麼這裏就會顯得很是冗餘,必須考慮重構,抽出共性代碼。並且若是須要打其餘折扣,修改的地方也不少。數組
面向對象的編程,並非類越多越好,類的劃分是爲了封裝,但分類的基礎是抽象,具備相同屬性和功能的對象的抽象集合纔是類。ide
/** * 現金收費抽象類 * Created by callmeDevil on 2019/6/1. */ public abstract class CashSuper { /** * 收取現金 * * @param money 原價 * @return 當前價 */ public abstract double acceptCash(double money); }
/** * 正常收費子類 * Created by callmeDevil on 2019/6/1. */ public class CashNormal extends CashSuper { @Override public double acceptCash(double money) { // 正常收費,原價返回 return money; } }
/** * 返利收費子類 * Created by callmeDevil on 2019/6/1. */ public class CashReturn extends CashSuper{ // 返利條件 private double moneyCondition = 0; // 返利值 private double moneyReturn = 0; // 返利收費,初始化時必須輸入返利條件和返利值,好比滿300返100, // 則moneyCondition 爲300,moneyReturn 爲100 public CashReturn(double moneyCondition, double moneyReturn) { this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double money) { double result = money; if (money >= moneyCondition) { // 若大於返利條件,則須要減去返利值 result = money - Math.floor(money / moneyCondition) * moneyReturn; } return result; } }
/** * 打折收費子類 * Created by callmeDevil on 2019/6/1. */ public class CashRebate extends CashSuper{ // 折扣率 private double moneyRebate = 1; public CashRebate(double moneyRebate) { // 打折收費,初始化時,必須輸入折扣率,如打八折,就是0.8 this.moneyRebate = moneyRebate; } @Override public double acceptCash(double money) { return money * moneyRebate; } }
/** * 現金收費工廠類 * Created by callmeDevil on 2019/6/1. */ public class CashFactory { /** * 建立現金收取工廠實例 * * @param type 收費類型 * @return */ public static CashSuper createCashAccept(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; default: break; } return cs; } }
/** * 現金收費測試 * Created by callmeDevil on 2019/6/1. */ public class CashTest { public static void main(String[] args) { double price = 400; double num = 3; System.out.println(String.format("單價:%s 元,數量:%s 個", price, num)); String type = "正常收費"; CashSuper cashSuper = CashFactory.createCashAccept(type); double total = cashSuper.acceptCash(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); type = "滿300減100"; cashSuper = CashFactory.createCashAccept(type); total = cashSuper.acceptCash(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); type = "打8折"; cashSuper = CashFactory.createCashAccept(type); total = cashSuper.acceptCash(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); } }
輸出結果單元測試
單價:400.0 元,數量:3.0 個 折扣:正常收費;總價:1200.0 元 折扣:滿300減100;總價:900.0 元 折扣:打8折;總價:960.0 元
簡單工廠模式雖然也可以解決問題2,但這個模式只是解決對象的建立問題,並且因爲工廠自己包括了全部的收費模式,商場是可能常常性的更改打折額度和返利額度,每次維護或擴展收費方式都要改動這個工廠,以至代碼須要從新編譯部署,這是很糟糕的,因此不是最好的解決辦法。測試
定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化,不回影響到使用算法的客戶。this
其實上面的簡單工廠模式實現方式裏面的CashSuper、CashNormal、CashRebate、CashReturn都不須要更改,只須要增長一個CashContext類,同時修改下客戶端就能夠了。code
/** * 現金上下文 * Created by callmeDevil on 2019/6/1. */ public class CashContext { private CashSuper cs = null; public CashContext(String type) { switch (type) { case "正常收費": cs = new CashNormal(); break; case "滿300減100": cs = new CashReturn(300, 100); break; case "打8折": cs = new CashRebate(0.8); break; default: break; } } public double getResult(double money) { return cs.acceptCash(money); } }
/** * 策略模式測試 * Created by callmeDevil on 2019/6/1. */ public class ContextTest { public static void main(String[] args) { double price = 400; double num = 3; System.out.println(String.format("單價:%s 元,數量:%s 個", price, num)); String type = "正常收費"; CashContext cashContext = new CashContext(type); double total = cashContext.getResult(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); type = "滿300減100"; cashContext = new CashContext(type); total = cashContext.getResult(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); type = "打8折"; cashContext = new CashContext(type); total = cashContext.getResult(price) * num; System.out.println(String.format("折扣:%s;總價:%s 元", type, total)); } }
策略模式測試類中的代碼與簡單工廠的很是類似,由於這裏將策略模式與簡單工廠模式作告終合,所以比較難以判斷策略模式的好處到底在哪。若是仔細分析一下會發現,只用簡單工廠的測試類中,也就是客戶端代碼耦合了CashSuper和CashFactory兩個類,而使用了策略模式的客戶端只涉及到了CashContext一個類,將客戶端與具體算法的實現進行了解耦,這樣若是商場須要變動促銷折扣時,除了變動具體的折扣實現類,只須要更改CashContext便可,客戶端徹底不用作任何更改,這就是策略模式帶來的最大好處。orm