前言:
最近在寫專案的時候,深感設計模式的重要性。一個人的程式碼寫的好不好,別人看的舒不舒服,和會不會設計模式緊密關聯的。之前看過四人幫的設計模式。但當時僅限於看,包括現在也僅限於看。有的時候專案中,你都不知道有沒有運用到了設計模式。也許用到了單例模式,但你並不知道如何用的,不知不覺就用到了。
《武林外傳》老白曾經說過這樣一句話。高手就是手裡無刀,心中也無刀。類似於設計模式,你不知不覺中已經融進你的程式碼中了,但你並不知已經運用了。當然我沒有達到這個境界,可能五年,十年,或者更久,誰也說不準呢。
這次正好趁這個專案,把用到的涉及模式總結一下,策略模式和模板方法模式。
1:設計模式分類
總體來說設計模式分為三大類:
建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。
我們這次講的都屬於行為型模式。這類模式負責物件間的高效溝通和職責委派。注意理解下物件間,責任委派。
2:策略模式
策略模式是一種行為設計模式, 它能讓你定義一系列演算法, 並將每種演算法分別放入獨立的類中, 以使演算法的物件能夠相互替換。
在我的專案中有這麼一個場景,大家也可以想象一個。使用者購買商品支付的時候,可能會使用多種不同的優惠券。比如說,貼息,滿減,隨機立減等等
這幾種返回給前端的金額,文案,以及個個方法都有比較大的差異。我們可以理解為三種不同的演算法,選擇哪種策略,完全由使用者有哪張優惠券所決定的。
因此很簡單的,我們可以使用策略模式。
1: 我們首先先定義一個介面
1 @Service 2 public interface CouponStrategy { 3 4 void execute(Coupon coupon); 5 6 }
2:定義不同的實現類
1 class CouponStrategyA implements CouponStrategy { 2 3 @Override 4 public void execute(Coupon coupon) { 5 // 第一種具體演算法 6 } 7 8 } 9 10 class CouponStrategyB implements CouponStrategy { 11 12 @Override 13 public void execute(Coupon coupon) { 14 // 第二種具體演算法 15 } 16 17 }
3:我們可以根據coupon選擇不同的演算法。這裡我們可以採用工廠模式
1 @Service 2 public class CouponStrategyFactory { 3 4 5 private Map<String, CouponStrategy> serviceMap = new HashMap<>(); 6 7 8 public CouponStrategy getService(CouponInfo couponInfo) { 9 10 CouponStrategy couponStrategy = serviceMap.getOrDefault(coupon.getCouponType(), couponDefault); 11 12 return couponStrategy; 13 } 14 15 }
最後,這種演算法看起來是非常的乾淨整潔舒服的。比如之前很多type=1,則演算法A。type=2則演算法B。程式碼雖然實現,但看起來很難受。
並且策略模式讓你能將不同行為抽取到一個獨立類層次結構中, 並將原始類組合成同一個, 從而減少重複程式碼。比如我可以把A,B,C演算法重複的都抽象到interface介面中,程式碼最重要一點就是避免寫重複性的程式碼。
3:模板模式
模板方法模式是一種行為設計模式, 它在超類中定義了一個演算法的框架, 允許子類在不修改結構的情況下重寫演算法的特定步驟。
比如我們JDK經典的ArrayList,用到的就是模板模式,我們看一下他的類圖如下:
這幅圖真的特別的經典,可以說是學習模板方法最好的實踐了。
ArrayList繼承了AbstractList並且實現了多種的介面。ArrayList只實現了自己特定的演算法,其餘比較通用的演算法全部由AbstractList實現。
比如addAll方法, 它位於absract介面。它定義了一系列的演算法,比如首先check,在for迴圈add等等。
但是具體怎麼add他並沒有實現,而是交由子類去具體實現。
1 public boolean addAll(int index, Collection<? extends E> c) { 2 rangeCheckForAdd(index); 3 boolean modified = false; 4 for (E e : c) { 5 add(index++, e); 6 modified = true; 7 } 8 return modified; 9 }
比如ArrayList的實現
1 public void add(int index, E element) { 2 rangeCheckForAdd(index); 3 4 ensureCapacityInternal(size + 1); // Increments modCount!! 5 System.arraycopy(elementData, index, elementData, index + 1, 6 size - index); 7 elementData[index] = element; 8 size++; 9 }
又比如LinkedList
1 public void add(int index, E element) { 2 checkPositionIndex(index); 3 4 if (index == size) 5 linkLast(element); 6 else 7 linkBefore(element, node(index)); 8 }
類似於子類可以通過鉤子操作可以控制父類的行為,是不是很神奇。
最後,當你只希望客戶端擴充套件某個特定演算法步驟, 而不是整個演算法或其結構時, 可使用模板方法模式。 模板方法將整個演算法轉換為一系列獨立的步驟, 以便子類能對其進行擴充套件, 同時還可讓超類中所定義的結構保持完整。
4:策略模式和模板模式同與異
這兩種模式非常的相像,一不留神可以分不清楚用那種模式了。當然這就是開頭所說的,心中有刀,手裡已無刀,你不知不覺中已經融進你的程式碼中了,但你並不知已經運用了。這其實就是最棒的結果了,但對於我們們初學者,剛開始弄清楚還是有必要的。
相同點:
1: 毋庸置疑都可以減少程式碼的重複,將重複程式碼抽象到父類即可。
2: 可以很容易的切換演算法,根據前端傳來的引數,具體演算法何種演算法,何種模式。
不同點:
1:模板模式基於繼承機制: 它允許你通過擴充套件子類中的部分內容來改變部分演算法。
2:策略模式基於組合機制: 你可以通過對相應行為提供不同的策略來改變物件的部分行為。 模板方法在類層次上運作, 因此它是靜態的。 策略在物件層次上運作, 因此允許在執行時切換行為。
3:模板更加看重是演算法的流程,全部已經規定好了,子類可以修改部分流程演算法
4:策略模式是將整個演算法全部重寫,不是部分,而是整體。
最後祝大家都能寫出漂亮,驚豔,眼前一亮的程式碼。最終做到,手裡無刀,心中也無。