模板方法模式

定义

    在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
    通俗理解就是:在一个基类里面,定义了一个算法的流程(里面调用了很多方法),其中一些方法是基类已经实现的,一些是需要具体子类或实现类来实现的,还有一下是钩子(允许子类或实现类重写,如果不重写就按默认的执行),那么那个基类的流程方法就是模板方法。即算法框架的轨迹已经定义好了,部分实现允许按实际情况自己定义。模块方法模式在框架的架构中大量使用。

生活案例

    在一个饮料冲泡的流程中,对于不同的饮料,很多方法都是相同的,只有部分实现是根据不同的饮料来实现的。所以可以定义一个基类:(prepareRecipe()为模板方法)
其中boilWater()和pourInCup()是每种饮料都需要的,所以基类来实现这两种方法,brew()和addCondiments()是根据不同饮料来实现的,所以定义为抽象类,需要子类来实现。

public abstract class CaffeineBeverage {
  
	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 Coffee extends CaffeineBeverage {
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
}

    钩子的定义:钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。  
    所以在饮料冲泡的流程中,对于要不要加入调料(即addCondiments)应该可以选择。
所以修改后的基类是:
public abstract class CaffeineBeverageWithHook {
 
	void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		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 CoffeeWithHook extends CaffeineBeverageWithHook {
 
	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 API中使用到的模板方法模式

    这个是Arrays的sort()方法(就是排序功能),注意,这个例子里面没有用到像上面那个例子的抽象父类,而是用了一个静态方法(在这里也就是模板方法),这个静态方法有调用了本类的方法(即基类已经实现好的方法),也有调用需要实现类(这里不是子类了,而是接口的实现类)实现的方法。所以整个sort()流程的框架是定义好的,需要的是根据不同的实现类来实现部分具体的方法。
    部分Arrays的源码:
 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) {
	int length = high - low;

	// Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
			 ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }

其中compareTo()方法是需要具体实现类来实现的,其他是基类Arrays已经实现的(有些常量和方法没贴出来,读者可以自行看java源码)。
    那么具体实现类是怎样的:
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;
		}
	}
}

具体实现类需要实现Comparable接口,因为在基类算法流程里面需要将这个类强转为Comparable类型;具体实现类也需要实现compareTo()这个方法,流程里面也需要调用这个方法(即部分需要根据具体实现类来实现的方法)。
    这样鸭子的排序流程就可以顺利进行了。

    模板方法模式整体就是这样,大家如果有什么想法想交流交流,欢迎在下面评论哦。微笑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值