1. 程式人生 > >Java進階篇設計模式之十一 ---- 策略模式和模板方法模式

Java進階篇設計模式之十一 ---- 策略模式和模板方法模式

前言

上一篇中我們學習了行為型模式的訪問者模式(Visitor Pattern)和中介者模式(Mediator Pattern)。本篇則來學習下行為型模式的兩個模式,策略模式(Strategy Pattern)和模板模式(Mediator Pattern)。

策略模式

簡介

策略模式(Strategy Pattern)屬於物件的行為模式。其用意是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響到客戶端的情況下發生變化。 其主要目的是通過定義相似的演算法,替換if else 語句寫法,並且可以隨時相互替換。

策略模式主要由這三個角色組成,環境角色(Context)、抽象策略角色(Strategy)和具體策略角色(ConcreteStrategy)。

  • 環境角色(Context):持有一個策略類的引用,提供給客戶端使用。
  • 抽象策略角色(Strategy):這是一個抽象角色,通常由一個介面或抽象類實現。此角色給出所有的具體策略類所需的介面。
  • 具體策略角色(ConcreteStrategy):包裝了相關的演算法或行為。

示例圖如下:

在這裡插入圖片描述

這裡為了方便理解,我們就拿剛學習Java的時候使用計算方法來說吧。 在使用計算器進行計算的時候,會經常用到加減乘除方法。如果我們想得到兩個數字相加的和,我們需要用到“+”符號,得到相減的差,需要用到“-”符號等等。雖然我們可以通過字串比較使用if/else寫成通用方法,但是計算的符號每次增加,我們就不得不加在原先的方法中進行增加相應的程式碼,如果後續計算方法增加、修改或刪除,那麼會使後續的維護變得困難。 但是在這些方法中,我們發現其基本方法是固定的,這時我們就可以通過策略模式來進行開發,可以有效避免通過if/else來進行判斷,即使後續增加其他的計算規則也可靈活進行調整。

首先定義一個抽象策略角色,並擁有一個計算的方法。


interface CalculateStrategy {
   int doOperation(int num1, int num2);
}
複製程式碼

然後再定義加減乘除這些具體策略角色並實現方法。

那麼程式碼如下:

class OperationAdd implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
   	return num1 + num2;
   }
}

class OperationSub implements CalculateStrategy {
   @Override
   public int do
Operation(int num1, int num2) { return num1 - num2; } } class OperationMul implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 * num2; } } class OperationDiv implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 / num2; } } 複製程式碼

最後在定義一個環境角色,提供一個計算的介面供客戶端使用。 程式碼如下:

class  CalculatorContext {
	private CalculateStrategy strategy;

	public CalculatorContext(CalculateStrategy strategy) {
		this.strategy = strategy;
	}

	public int executeStrategy(int num1, int num2) {
		return strategy.doOperation(num1, num2);
	}
}
複製程式碼

編寫好之後,那麼我們來進行測試。 測試程式碼如下:

	public static void main(String[] args) {
  		   int a=4,b=2;
		  CalculatorContext context = new CalculatorContext(new OperationAdd());    
	      System.out.println("a + b = "+context.executeStrategy(a, b));
	 
	      CalculatorContext context2 = new CalculatorContext(new OperationSub());      
	      System.out.println("a - b = "+context2.executeStrategy(a, b));
	 
	      CalculatorContext context3 = new CalculatorContext(new OperationMul());    
	      System.out.println("a * b = "+context3.executeStrategy(a, b));
	
	      CalculatorContext context4 = new CalculatorContext(new OperationDiv());    
	      System.out.println("a / b = "+context4.executeStrategy(a, b));
}
複製程式碼

輸出結果:

  		    a + b = 6
			a - b = 2
			a * b = 8
			a / b = 2
複製程式碼

策略模式優點:

擴充套件性好,可以在不修改物件結構的情況下,為新的演算法進行新增新的類進行實現; 靈活性好,可以對演算法進行自由切換;

策略模式缺點:

使用策略類變多,會增加系統的複雜度。; 客戶端必須知道所有的策略類才能進行呼叫;

使用場景:

如果在一個系統裡面有許多類,它們之間的區別僅在於它們的行為,那麼使用策略模式可以動態地讓一個物件在許多行為中選擇一種行為; 一個系統需要動態地在幾種演算法中選擇一種; 如果一個物件有很多的行為,如果不用恰當的模式,這些行為就只好使用多重的條件選擇語句來實現;

模板模式

簡介

模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但呼叫將以抽象類中定義的方式進行。 這種型別的設計模式屬於行為型模式。定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。

模板模式,其主要的的思想就是做一個模板,提供給客戶端進行呼叫。除去生活中我們經常用到的簡歷模板、合同模板等等,Java中也有很經典的模板使用,那就是Servlet,HttpService類提供了一個service()方法,這個方法呼叫七個do方法中的一個或幾個,完成對客戶端呼叫的響應。這些do方法需要由HttpServlet的具體則由子類提供。

模板模式主要由抽象模板(Abstract Template)角色和具體模板(Concrete Template)角色組成。

  • 抽象模板(Abstract Template): 定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟;定義並實現了一個模板方法。這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能呼叫一些具體方法。

  • 具體模板(Concrete Template): 實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟;每一個抽象模板角色都可以有任意多個具體模板角色與之對應,而每一個具體模板角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。

示例圖如下:

https://www.dofactory.com/images/diagrams/net/template.gif

這裡為了方便理解,我們依舊使用一個簡單的示例來加以說明。 我們以前在玩魂鬥羅、雙截龍、熱血物語、忍者神龜等等遊戲的時候,都需要在小霸王遊戲機上插卡,然後啟動遊戲才能玩,其中魂鬥羅這種遊戲,啟動遊戲之後就可以直接玩了,但是忍者神龜這種遊戲則在啟動遊戲之後,需要選擇其中一個角色才能開始玩。那麼我們可以根據這個場景寫出一個通用的模板,主要包含啟動遊戲,玩遊戲,結束遊戲這幾個必須實現的方法,選擇人物這個方法改成可選。

那麼這個抽象類的程式碼如下:

abstract class  Game{
	
	//啟動遊戲
	protected abstract void  runGame();
	//選擇人物
	protected  void choosePerson() {};
	//開始玩遊戲
	protected abstract void startPlayGame();
	//結束遊戲
	protected abstract void endPlayGame();
	
	//模板方法
	public final void play() {
		runGame();
		choosePerson();
		startPlayGame();
		endPlayGame();
	}
	
}
複製程式碼

定義好該抽象類之後,我們再來定義具體模板實現類。這裡定義兩個遊戲類,一個是魂鬥羅,一個忍者神龜。

那麼程式碼如下:

class ContraGame extends Game{

   @Override
   protected void runGame() {
   	System.out.println("啟動魂鬥羅II...");
   }

   @Override
   protected void startPlayGame() {
   	System.out.println("1P正在使用S彈打aircraft...");
   }

   @Override
   protected void endPlayGame() {
   	System.out.println("1P被流彈打死了,遊戲結束!");
   }
}

class TMNTGame extends Game{

   @Override
   protected void runGame() {
   	System.out.println("啟動忍者神龜III...");
   }

   @Override
   protected void choosePerson() {
   	System.out.println("1P選擇了Raph !");
   }

   @Override
   protected void startPlayGame() {
   	System.out.println("Raph正在使用絕技 “火箭頭槌” ");
   }

   @Override
   protected void endPlayGame() {
   	System.out.println("Raph 掉進井蓋裡死了,遊戲結束了! ");
   }
}

複製程式碼

最後再來進行測試,測試程式碼如下:


public static void main(String[] args) {
   	Game game = new ContraGame();
   	game.play();
   	System.out.println();
   	game = new TMNTGame();
   	game.play();

}

複製程式碼

輸出結果:

啟動魂鬥羅II...
1P正在使用S彈打aircraft...
1P被流彈打死了,遊戲結束!

啟動忍者神龜III...
1P選擇了Raph !
Raph正在使用絕技 “火箭頭槌” 
Raph 掉進井蓋裡死了,遊戲結束了! 

複製程式碼

模板模式優點:

擴充套件性好,對不變的程式碼進行封裝,對可變的進行擴充套件; 可維護性好,因為將公共程式碼進行了提取,使用的時候直接呼叫即可;

模板模式缺點:

因為每一個不同的實現都需要一個子類來實現,導致類的個數增加,會使系統變得複雜;

使用場景:

有多個子類共有邏輯相同的方法; 重要的、複雜的方法,可以考慮作為模板方法。

注意事項:

為防止惡意操作,一般模板方法都加上 final 關鍵詞!

其它

音樂推薦

分享一首非常好聽的輕音樂! 網易雲網友評論:

簡單,重複,毫無華麗旋律,也無厚重悲涼的伴奏。但心偏偏就被緊緊的抓住了。一種茫然卻被迫緊湊的感覺。一種不知何所處的心虛。what for?

專案的程式碼

java-study 是本人在學習Java過程中記錄的一些程式碼,也包括之前博文中使用的程式碼。如果感覺不錯,希望順手給個start,當然如果有不足,也希望提出。 github地址: github.com/xuwujing/ja…

原創不易,如果感覺不錯,希望給個推薦!您的支援是我寫作的最大動力! 版權宣告: 作者:虛無境
部落格園出處:www.cnblogs.com/xuwujing CSDN出處:blog.csdn.net/qazwsxpcm  個人部落格出處:www.panchengming.com