您的位置:首页 > 其它

模版方法模式

2015-06-14 20:59 204 查看

一.前言

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

自动脑补一下,以上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: