模板方法模式定义
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
模板方法非常常见,对创建框架来说,由框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。
举个咖啡和茶的例子
用模版方法实现代码复用,让茶,咖啡等一类制作方法类似的饮料借用一个算法框架来实现
茶的制作方法:1.把水煮沸 2.用沸水浸泡茶叶 3.把茶倒进杯子4.加柠檬
咖啡的制作方法:1.把水煮沸 2.用沸水冲泡咖啡 3.把咖啡倒进杯子4.加牛奶或者糖
可以看出两者的制作过程其实是采用了相同的算法:
1.把水煮沸
2.用沸水冲泡咖啡或者茶
3.把饮料倒进杯子
4.加入调料
我们把这个咖啡因类饮料的制作方法抽象出来 实现一个模板方法,模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供实现
public abstract class CaffeineBeverage {
//prepareRecipe是我们的模板方法
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 CaffeineBeverage {
public void brew() {
System.out.println("Steeping the tea");
}
public void addCondiments() {
System.out.println("Adding Lemon");
}
}
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");
}
}
对模板方法进行挂钩
钩子(hook)是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
某些步骤是可选的,所以可以将这些步骤实现成钩子,而不是实现成抽象方法,这样就可以让抽象类的子类的负荷减轻。
比如,也可以利用钩子做条件控制,影响抽象类中的算法流程:钩子方法在抽象类中有默认实现返回true,放在抽象类的if条件语句中,子类可以覆盖也可以不覆盖这个钩子方法。
上述例子可以对ddCondiments()方法进行挂钩,让子类自由选择要不要加调料,抽象类重写如下:
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;
}
}
子类需要实现customerWantsCondiments方法,进行判断是否要进行加调料的操作。而实现这个方法要根据顾客的需求来定,getUserInput()方法得到顾客输入的需求信息
//咖啡子类 挂钩版
import java.io.*;
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;
}
}
茶的子类挂钩
import java.io.*;
public class TeaWithHook extends CaffeineBeverageWithHook {
public void brew() {
System.out.println("Steeping the tea");
}
public void addCondiments() {
System.out.println("Adding Lemon");
}
//对判断条件的实现 如果输入y就加入调料
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 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;
}
}
//测试
public class BeverageTestDrive {
public static void main(String[] args) {
Tea tea = new Tea();
Coffee coffee = new Coffee();
System.out.println("\nMaking tea...");
tea.prepareRecipe();
System.out.println("\nMaking coffee...");
coffee.prepareRecipe();
TeaWithHook teaHook = new TeaWithHook();
CoffeeWithHook coffeeHook = new CoffeeWithHook();
System.out.println("\nMaking tea...");
teaHook.prepareRecipe();
System.out.println("\nMaking coffee...");
coffeeHook.prepareRecipe();
}
模版方法模式是一个很常见的模式,这是因为对创建框架来说,这个模式很方便可以做到有框架控制如何做事情,而由你指定框架中每个步骤的细节,在实际应用中模版方法有很多实现方法。
下面的例子是用模版方法进行排序
class Student implements Comparable<Student>{
private String name;
private int age;
private float score;
public Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
public String toString()
{
return name+"\t\t"+age+"\t\t"+score;
}
@Override
public int compareTo(Student o) {
if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部
return -1;//由高到底排序
else if(this.score<o.score)
return 1;
else{
if(this.age>o.age)
return 1;//由底到高排序
else if(this.age<o.age)
return -1;
else
return 0;
}
}
}
public class ComparableDemo01 {
public static void main(String[] args) {
Student stu[]={new Student("zhangsan",20,90.0f),
new Student("lisi",22,90.0f),
new Student("wangwu",20,99.0f),
new Student("sunliu",22,100.0f)};
Arrays.sort(stu);
for(Student s:stu)
{
System.out.println(s);
}
}
}
排序的实现看起来更像是策略模式,因为策略模式使用对象组合,但是在策略模式中,你所组合的类实现了整个算法。数
组所实现的排序算法并不完整,它需要一个类填补 compareTo()方法的实现。因此认为这更像模版方法
策略模式
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
这个原则的思考方式是,把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分
这个模式涉及到三个角色:
● 环境(Context)角色:持有一个Strategy的引用。
● 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
● 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
策略模式网上的例子有很多,也比较简单,与上面一个排序做对比,下面用comparator实现的排序用的就是策略模式
import java.util.Comparator;
class Student {
private String name;
private int age;
private float score;
public Student(String name, int age, float score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public String toString()
{
return name+"\t\t"+age+"\t\t"+score;
}
}
class StudentComparator implements Comparator<Student>{
public int compare(Student o1, Student o2) {
if(o1.getScore()>o2.getScore())
return -1;
else if(o1.getScore()<o2.getScore())
return 1;
else{
if(o1.getAge()>o2.getAge())
return 1;
else if(o1.getAge()<o2.getAge())
return -1;
else
return 0;
}
}
}
public class comparatorDemo1 {
public static void main(String[] args) {
Student stu[]={new Student("zhangsan",20,90.0f),
new Student("lisi",22,90.0f),
new Student("wangwu",20,99.0f),
new Student("sunliu",22,100.0f)};
java.util.Arrays.sort(stu,new StudentComparator());
for(Student s:stu)
{
System.out.println(s);
}
}
}
<span style="font-size:18px;">StudentComparator类完整地实现了整个排序应用的算法,所以它是策略模式</span>
策略模式和模版方法模式对比
1.两者的意图不同。策略模式的意图是定义一个算法家族,把每个算法封装起来并让这些算法可以互换,这样客户可以轻易使用不同的算法。模版方法模式的意图是在抽象类定义一个算法大纲,而由子类定义其中某些步骤的内容,算法的个别步骤可以有不同的实现细节而算法的结果始终维持不变,达到代码的复用效果。
2.实现方法不同。策略模式是通过对象组合的方式实现的,而模版方法模式是通过继承实现的
3.各种的优势不同。模版方法对算法有更多的控制权,它的类的效率会比策略高效,会重复使用到的代码,都被放进超类,好让所有的子类共享,这使得它经常被用于创建框架。策略模式使用对象组合,因此更有弹性,可以方便选择使用不同的算法,加入新的算法也很方便,不需要依赖超类中方法的实现