模版方法模式

一.前言

what is templatemethod,一句话我的理解:
在超类并定义了算法的步骤,定义了共有步骤的具体实现,并允许子类为一个或者多个步骤提供实现。
解释:比如在超类中定义算法步骤包括5步,其中1、3、5是在超类中实现的,2、4的实现在子类中有不同,所以子类各自实现。



二.简单的例子

泡茶和冲咖啡,他们的步骤分别是:
泡茶:
1.把水煮沸
2.用沸水浸泡茶叶
3.把茶水倒进杯子
4.加柠檬
冲咖啡:
1.把水煮沸
2.用沸水冲泡咖啡
3.把咖啡倒进杯子

4.加糖和牛奶


对应的代码如下:

public class Tea {
 
	void prepareRecipe() {
		boilWater();
		steepTeaBag();
		pourInCup();
		addLemon();
	}
 
	public void boilWater() {
		System.out.println("Boiling water");
	}
 
	public void steepTeaBag() {
		System.out.println("Steeping the tea");
	}
 
	public void addLemon() {
		System.out.println("Adding Lemon");
	}
 
	public void pourInCup() {
		System.out.println("Pouring into cup");
	}
}

public class Coffee {
 
	void prepareRecipe() {
		boilWater();
		brewCoffeeGrinds();
		pourInCup();
		addSugarAndMilk();
	}
 
	public void boilWater() {
		System.out.println("Boiling water");
	}
 
	public void brewCoffeeGrinds() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void pourInCup() {
		System.out.println("Pouring into cup");
	}
 
	public void addSugarAndMilk() {
		System.out.println("Adding Sugar and Milk");
	}
}

很明显,这两个类是存在了重复代码的,既然他们步骤是类似的,应该将共同的部分抽取出来,放入一个基类中。我们不难得出下面的关系:



饮料超类:

public abstract class Beverage {
  
	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}
 
	abstract void brew();
  
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
  
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
}

茶:

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");
	}
}

想到有个问题,像我们家妹纸,喝东西都喜欢原味的,我这强制加了Condiment好像不好诶,肿么办,为了我们家妹纸,当然不能提供这么愚蠢的设计,有办法的,我们使用钩子,改造上述代码改造如下:

public abstract class BeverageWithHook {
 
	void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		//在这里控制是否执行addCondiments
		if (customerWantsCondiments()) {
			addCondiments();
		}
	}
 
	abstract void brew();
 
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
 
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
	
	//默认为都需要加调味料
	boolean customerWantsCondiments() {
		return true;
	}
}

//拥有钩子的茶
public class TeaWithHook extends BeverageWithHook {
 
	public void brew() {
		System.out.println("Steeping the tea");
	}
 
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
	
	//在代码中添加供用户输入的部分,模拟用户的选择
	private String getUserInput() {
		// get the user's response
		String answer = null;

		System.out.print("Would you like lemon with your tea (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}

//类似的,拥有钩子的咖啡
public class CoffeeWithHook extends BeverageWithHook {
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
 
	private String getUserInput() {
		String answer = null;

		System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}

上述的钩子控制了是否向饮料中加入调味料。总结一下上面的思路:
当子类必须提供算法中某个方法或者步骤的实现时,就使用抽象方法。如果这个算法是可选的,就使用钩子,子类也可以覆盖钩子,但不强制这么做。


一个好玩的原则,好莱坞原则:

别打电话给我,我会打电话给你。
翻译一下就是:
别调用我们,我们会调用你。
在模版方法中,就是基类调用了子类的方法。


三.排序方法对于模版方法的运用

java的数组类提供了一个模版的排序方法sort,源码如下:

public static void sort(Object[] a){
	Object aux[] = (Object [])a.clone();
	mergeSort(aux,a,0,a.length,0);
}

private static void mergeSort(Object src[],Object dest[],int low,int high,int off){
	for(int i=low;i<high;int off){
		for(int j=i;j>low&&((Comparable) dest[j-1]).compareTo((Comparable) dest[j])>0;j--){
			swap(dest,j,j-1);
		}
	}
}

从上面都代码可以看出,会调用数组中元素的compareTo方法,这个方法就需要在元素子类中实现这个方法,例如排序一个鸭子类:

public class Duck implements Comparable {
	String name;
	int weight;
  
	public Duck(String name, int weight) {
		this.name = name;
		this.weight = weight;
	}
 
	public String toString() {
		return name + " weighs " + weight;
	}
 
 
  
	public int compareTo(Object object) {
 
		Duck otherDuck = (Duck)object;
  
		if (this.weight < otherDuck.weight) {
			return -1;
		} else if (this.weight == otherDuck.weight) {
			return 0;
		} else { // this.weight > otherDuck.weight
			return 1;
		}
	}
}

测试函数如下:

public class DuckSortTestDrive {

	public static void main(String[] args) {
		Duck[] ducks = { 
						new Duck("Daffy", 8), 
						new Duck("Dewey", 2),
						new Duck("Howard", 7),
						new Duck("Louie", 2),
						new Duck("Donald", 10), 
						new Duck("Huey", 2)
		 };

		System.out.println("Before sorting:");
		display(ducks);

		Arrays.sort(ducks);
 
		System.out.println("\nAfter sorting:");
		display(ducks);
	}

	public static void display(Duck[] ducks) {
		for (int i = 0; i < ducks.length; i++) {
			System.out.println(ducks[i]);
		}
	}
}

这个跟模版方法虽然不能完全重合,但是基本思想是符合的:
1.鸭子数组
2.调用Array.sort
3.实现数组中鸭子的compareTo



自动脑补一下,以上。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值