1. 程式人生 > >模板方法模式——Template Method Pattern

模板方法模式——Template Method Pattern

模板方法模式
模擬製作飲料的應用:
沖泡飲料時,我們需要遵循下面的沖泡步驟,即
(1)把水煮沸
(2)用沸水沖泡
(3)把沖泡後的飲料倒進杯子
(4)加入適當的調料

另外,對於不同的飲料,步驟(2)沖泡方法和步驟(4)的加入調料的方法是不同的
因此需要這兩個方法設計為抽象方法,程式碼如下:
public abstract class Beverage
{
	void boilWater()
	{
		System.out.println("Boiling water.");
	}								//(1)把水煮沸
	void pourInCup()
	{
		System.out.println("Pouring into cup.")
	}								//(3)把飲料倒進杯子
	abstract void brew();			//(2)用沸水沖泡
	abstract void addCondiments();	//(4)加入適當的調料
}
我們有兩種飲料需要使用這個沖泡飲料的方法,茶和咖啡,程式碼如下:
public class Tea extends Beverage
{
	public void brew()
	{
		System.out.println("Steeping the tea.");
	}//浸泡茶葉
	public void addCondiments()
	{
		System.out.println("Adding Lemon.");
	}//加入檸檬
}
public class Coffee extends Beverage
{
	public void brew()
	{
		System.out.println("Dripping Coffee through filter.");
	}//沖泡咖啡
	public void addCondiments()
	{
		System.out.println("Adding Sugar and Milk.");
	}//加入糖和牛奶
}
現在我們可以來製作茶和咖啡了,程式碼如下:
public class MarkBeverage
{
	Tea tea = new Tea();
	tea.boilWater();
	tea.brew();
	tea.pourInCup();
	tea.addCondiments();
	Coffee coffee = new Coffee();
	coffee.boilWater();
	coffee.brew();
	coffee.pourInCup();
	coffee.addCondiments();
}
一切看起來都很正常,可是,如果要新建多個物件,那麼我們就要寫多個方法呼叫
為了解決這個問題,我們可以將製作飲料的四個步驟寫在一個方法裡面,程式碼如下:

public abstract class Beverage
{
	void make()
	{
		boilWater();		//(1)把水煮沸
		brew();				//(2)用沸水沖泡
		pourInCup();		//(3)把飲料倒進杯子
		addCondiments();	//(4)加入適當的調料
	}
	void boilWater()
	{
		System.out.println("Boiling water.");
	}								//(1)把水煮沸
	void pourInCup()
	{
		System.out.println("Pouring into cup.")
	}								//(3)把飲料倒進杯子
	abstract void brew();			//(2)用沸水沖泡
	abstract void addCondiments();	//(4)加入適當的調料
}
這樣做看起來清晰多了,使用時也變得相對簡單,而且也達到了封裝的效果:
public class MarkBeverage
{
	Tea tea = new Tea();
	tea.mark();
	Coffee coffee = new Coffee();
	coffee.mark();
}
這就是模板方法模式
模板方法模式:在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中
                         子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟
程式碼演示:

public abstract class AbstractClass
{
	final void templateMethod
	{
		methodOne();
		methodTwo();
		/*------第N個普通方法------*/  
		abstractMethodOne();
		abstractMethodTwo();
		/*------第N個抽象方法------*/  
	}
	//不需要改變的步驟使用final宣告
	final void methodOne(){}
	final void methodTwo(){}
	//可改變的步驟宣告為抽象方法,在子類裡重新定義
	abstract abstractMethodOne();
	abstract abstractMethodTwo();
}
另外,需要考慮到其它的一些情況,如,並不是所有人都會喝加牛奶的咖啡
那麼,上例中的第個四步驟並不是必須的,這時我們就要加入一個判斷語句:

public abstract class Beverage
{
	void make()
	{
		boilWater();			//(1)把水煮沸
		brew();					//(2)用沸水沖泡
		pourInCup();			//(3)把飲料倒進杯子
		if(isCustomerWantsCondiments())
		{
			addCondiments();	//(4)加入適當的調料
		}
	}
	void boilWater()
	{
		System.out.println("Boiling water.");
	}								//(1)把水煮沸
	void pourInCup()
	{
		System.out.println("Pouring into cup.")
	}								//(3)把飲料倒進杯子
	boolean isCustomerWantsCondiments()
	{
		return true;
	}//新加入的方法,用於判斷是否加調料
	abstract void brew();			//(2)用沸水沖泡
	abstract void addCondiments();	//(4)加入適當的調料
}
現在我們知道了並不總是使用模板方法中的所有方法
為了解決這個問題,我們需要對模板方法進行掛鉤,即使用鉤子方法
上例中的isCustomerWantsCondiments()就是一個鉤子
Swing窗體例項:
//此處省略import語句
public class MyFrame extends JFrame
{
	//update()方法為模板方法Template method
	public MyFrame(String title)
	{
		super(title);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setSize(300,300);
	}//初始化窗體
	public void paint(Graphics graphics)
	{
		super.paint(graphics);
		graphics.drawString("Hello",100,100);
	}//paint()方法為鉤子方法Hook method
	public static void main(String args[])
	{
		MyFrame frame = new MyFrame("Hello!"); 
	}
}

模板方法定義了演算法的步驟,並把這些步驟的實現延遲到子類
模板方法的抽象類可以定義具體方法,抽象方法和鉤子方法
抽象方法是可變步驟,由子類實現
鉤子方法可以控制某些步驟是否需要執行
模板方法用於封裝演算法並實現程式碼的複用


參考書籍《Head First 設計模式》