【设计模式】之模板方法(Template Method)

模板方法的定义为:再一个操作中定义一个算法的骨架,将算法中的一些步骤延迟到子类去实现。模板方法允许子类在不该变算法结构的情况下重新定义算法的某些步骤。

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

在实际的面向对象设计中,模板方法的使用应该是非常广泛的。

模板方法一个很重要的作用,就是提高代码复用性,避免代码冗余。如果出现重复代码,就应该考虑是不是设计出错了。(When we got code duplication, that's a good sign we need to clean up the design)。

模板方法,顾名思义就是一个方法(method),在这个方法内部定义了一个算法的模板,算法的结构是不会变化的,算法中不变的部分会在模板方法所在的类中封装,依赖于具体实现的部分则在其他地方定义。

模板方法符合Holleywood Principle,即 "Don't call us, we'll call you“。


public abstract class CaffeineBeverageWithHook {
    final 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;
    }
}
CaffeineBeverageWithHook定义是一个抽象类,prepareRecipe方法就是一个模板方法,为了不让子类改变方法的结构,用关键字final修饰后,子类便无法override模板方法。

prepareRecipe内部定义一个算法,算法中调用了两个抽象方法brew和boilWater,所以这个算法是抽象的,它必须依赖与具体的实现才能运行。


在模板方法中还有一个比较重要的概念就是Hook,所谓Hook的定义如下:

hook operations, which provide default behavior that subclass can extend if necessary. A hook operation often does nothing by default. -- <Design Patterns>

A hook is a method that is declared in the abstract class, but only given an empty and default implementation. -- <Head First Design Patterns>

类CaffeineBeverageWithHook中的customerWantsCondiments就是一个hook操作。


customerWantsCondiments的具体实现定义在CaffeineBeverageWithHook的子类中。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class CoffeeWithHook extends CaffeineBeverageWithHook {
    public void brew() {
        System.out.println("Dripping Coffee though filter");
    }

    public void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }

    public boolean customerWantsCondiments() {
        String answer = getUserInput();
        return answer.toLowerCase().startsWith("y");
    }
    
    private String getUserInput() {
        String answer = null;
        System.out.println("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;
    }
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class TeaWithHook extends CaffeineBeverageWithHook {
    public void brew() {
        System.out.println("Steeping the tea");
    }

    public void addCondiments() {
        System.out.println("Adding Lemon");
    }

    public boolean customerWantsCondiments() {
        String answer = getUserInput();
        return answer.toLowerCase().startsWith("y");
    }
    
    private String getUserInput() {
        String answer = null;
        System.out.println("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 BeverageTestDrive {
    public static void main(String[] args) {
        TeaWithHook teaHook = new TeaWithHook();
        CoffeeWithHook coffeeHook = new CoffeeWithHook();

        System.out.println("\nMaking tea...");
        teaHook.prepareRecipe();
        
        System.out.println("\nMaking coffee...");
        coffeeHook.prepareRecipe();
    }
}

运行结果如下:



当然,模板方法的实现并非都是通过的继承的方式实现。

以下例子便是再模板方法中调用了其他类的方法。

import java.util.Arrays;
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 (Duck duck : ducks) {
            System.out.println(duck);
        }
    }
}

Arrays的静态方法sort便是一个模板方法,它需要调用传入参数对象的comareTo方法。

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 + " weights " + weight;
    }

    @Override
    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;
        }
    }
}

模板方法模式与策略模式有相似点,他们都封装了一个算法,但是模板放发只是抽象了算法的一部分,而策略模式抽象的是整个算法,而且策略模式是同哦那天过组合(Composition)实现,模板方法模式,主要是利用了inheritance。

书中有写:The template method calls primitive operations as well as operations defined in AbstractClass or those of others.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值