先不管模式, 把他和他的名字都忘了, 來看看問題 和 設計思路. 為啥要這麼做. 有一家店鋪, 裡面有一個售貨員, 售貨員當然是要賣東西的啦, 客戶進來買完東西, 找售貨員結賬, 那售貨員得知道一共多少錢吧? 一. 初步設計 商品類: 由於價格我使用的是 Long 類型, 所以, 要有一個轉換輸出的 ...
前言:
先不管模式, 把他和他的名字都忘了, 來看看問題 和 設計思路. 為啥要這麼做.
場景:
有一家店鋪, 裡面有一個售貨員, 售貨員當然是要賣東西的啦, 客戶進來買完東西, 找售貨員結賬, 那售貨員得知道一共多少錢吧?
一. 初步設計
商品類:
package org.elvin.strategy; /***/ public class Goods { /** * 商品名 */ private String name; /** * 商品價格 */ private Long Price; public Goods() { } public Goods(String name, Long price) { this.name = name; Price = price; } //region getter / setter public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getPrice() { return Price; } public void setPrice(Long price) { Price = price; } //endregion }
由於價格我使用的是 Long 類型, 所以, 要有一個轉換輸出的方法.
package org.elvin.strategy; import java.text.MessageFormat; public class MoneyUtils { public static String getYuan(Long money){ Long yuan = money / 100; Long jiao = money % 100 / 10; Long fen = money % 10; return MessageFormat.format("{0}.{1}{2}", yuan, jiao , fen ); } }
售貨員:
package org.elvin.strategy; import org.elvin.strategy.calculator.*; import org.junit.Test; import java.util.ArrayList; import java.util.List; /** * 環境角色(Context)*/ public class Seller { /** * 姓名 */ private String name; /** * 編號 */ private String code;
//region getter / setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; }
//endregion /** * 售賣商品 */ public void sellGoods(List<Goods> goods){ Long sum = 0L; for (Goods good : goods) { sum += good.getPrice(); } System.out.println("應付款: " + MoneyUtils.getYuan(sum) + " 元"); } @Test public void func1(){ List<Goods> goods = new ArrayList<>(); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("餅干", 250L)); goods.add(new Goods("辣條", 300L)); sellGoods(goods); } }
來看一下計算結果:
得到結果了, 沒啥毛病, 挺好.
今天老闆過生日, 突然想到, 要不要再店裡搞個活動, 來個優惠活動, 有些商品減價銷售.
明天老闆娘過生日, 老闆娘說, 老娘高興, 你減價銷售, 明天我來個免單銷售.
那現在咋辦呢? 明天過後, 價格肯定又要恢復到正常價格. what the fuck!
Seller類寫死了, 難道我在裡面加幾個計算方法?
加進去貌似可以解決當下問題, 但是, 後天如果老闆的老丈人過生日呢? 咋搞?
Ok, 到這裡, 差不多, 需要請出今天的大神 : 策略模式. 讓他來幫我們解決這個問題吧.
二. 設計改造
對於售貨員來說, 她必須知道, 今天該怎麼計算價格, 是優惠還是不優惠, 或者是滿多少錢, 送東西什麼的.
那麼, 將這些優惠或者說價格的計算方法抽象出來, 成為一個介面或者抽象類.
讓優惠或者不優惠實現或者繼承他.
實現方式:
這裡, 我將他抽象為一個介面
package org.elvin.strategy.calculator; import org.elvin.strategy.Goods; import java.util.List; /** * 抽象策略角色(Strategy) * 優惠介面
*/ public interface PreferentialPrice { public void getPrice(List<Goods> goods); }
PreferentialPrice 需要作為一個屬性,出現在 Seller 類中.
在Seller中加入
/** * 計算優惠後的價格 * 抽象角色, 次角色給出所有具體策略類所需的介面 */ private PreferentialPrice preferentialPrice; public PreferentialPrice getPreferentialPrice() { return preferentialPrice; } public void setPreferentialPrice(PreferentialPrice preferentialPrice) { this.preferentialPrice = preferentialPrice; }
這裡提供三種計算方式:
1. 正常方式
/** * 具體策略角色(ConcreteStrategy)
*/ public class NoPreferential implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { Long sum = 0L; for (Goods good : goods) { sum += good.getPrice(); } System.out.println("應付款: " + MoneyUtils.getYuan(sum) + " 元"); } }
2. 免單方式
/** * 具體策略角色(ConcreteStrategy)
*/ public class Free implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { System.out.println("免單, 不要錢 !"); } }
3. 部分優惠方式
/** * 具體策略角色(ConcreteStrategy)
*/ public class ReduceSomeGoods implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { Long sum = 0L; for (Goods good : goods) { switch (good.getName()) { case "泡麵": sum += good.getPrice() - 50L; break; case "火腿": sum += good.getPrice() - 20L; break; case "雞蛋": sum += good.getPrice() - 10L; break; default: sum += good.getPrice(); break; } } System.out.println("應付款: " + MoneyUtils.getYuan(sum) + " 元"); } }
將Seller類中, 計算的方法修改一下:
public void sellGoods(List<Goods> goods){ if(preferentialPrice == null){ setPreferentialPrice(new NoPreferential()); } preferentialPrice.getPrice(goods); }
在計算的時候, 如果沒有傳入優惠, 則預設使用無優惠方式
再看測試方法:
@Test public void func1(){ List<Goods> goods = new ArrayList<>(); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("泡麵", 550L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("雞蛋", 70L)); goods.add(new Goods("餅干", 250L)); goods.add(new Goods("辣條", 300L)); setPreferentialPrice(new Free()); sellGoods(goods); System.out.println("-----------------------"); setPreferentialPrice(new ReduceSomeGoods()); sellGoods(goods); System.out.println("-----------------------"); setPreferentialPrice(new NoPreferential()); sellGoods(goods); }
結果:
策略模式作為一種對象行為模式, 在這裡應該還是體現到了吧.
那總結一下?給個不容易懂得(網上抄的):
策略模式屬於對象的行為模式。其用意是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響到客戶端的情況下發生變化。
說的比較抽象, 來個具體的吧:
一帥哥喜歡約妹子, 那咋約出來呢? 不是所有的妹子都喜歡吃飯看電影吧. 那針對不同的妹子, 使用不同的方法來約. 約喜歡看電影的妹子看電影, 約喜歡吃小吃的妹子吃小吃.
那吃飯, 看電影, 吹海風...... 等等, 這些手段, 目的都是為了讓妹子做他女朋友(這裡不討論時間). 目的不變, 手段層出不窮. 這些方法, 就可以理解為不同的 strategy.
通過上面的例子, 可以看出, 具體的演算法與演算法之間沒有依賴關係, 都是平等的(平等性), 可以相互替換的. 那在運行的時候, 每次都只能使用一種(唯一性).