策略模式和模板方法同與異

wenbochang發表於2021-01-10

前言:

  最近在寫專案的時候,深感設計模式的重要性。一個人的程式碼寫的好不好,別人看的舒不舒服,和會不會設計模式緊密關聯的。之前看過四人幫的設計模式。但當時僅限於看,包括現在也僅限於看。有的時候專案中,你都不知道有沒有運用到了設計模式。也許用到了單例模式,但你並不知道如何用的,不知不覺就用到了。

  《武林外傳》老白曾經說過這樣一句話。高手就是手裡無刀,心中也無刀。類似於設計模式,你不知不覺中已經融進你的程式碼中了,但你並不知已經運用了。當然我沒有達到這個境界,可能五年,十年,或者更久,誰也說不準呢。

  這次正好趁這個專案,把用到的涉及模式總結一下,策略模式和模板方法模式。

 

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:策略模式是將整個演算法全部重寫,不是部分,而是整體。

 

最後祝大家都能寫出漂亮,驚豔,眼前一亮的程式碼。最終做到,手裡無刀,心中也無。

 

 

 

 

相關文章