title: Template Method模式
tag: 笔记 设计模式
Template Method模式
Template Method
模式是带有模板功能的模式组成模板的方法定义在父类中。由于这些方法是抽象方法,所以仅仅查看父类是不知道这些方法是如何进行具体处理的,我们只知道父类是符合调用它的。
实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就决定了具体的处理。也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时程序行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中所定义的那样进行。
像这样在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为Template Method
模式。
示例程序
该实例程序会将字符和字符串循环显示5次的简单程序,我们会使用模板方法模式使字符和字符串有不同的处理。
该程序中有下面三个类:
AbstractDisplay
open
,print
,close
三个抽象方法display
,调用了其它三个抽象方法,我们叫这个方法模板方法。
CharDisplay
:继承AbstractDisplay
并实现抽象方法,处理字符。StringDisplay
:继承AbstractDisplay
并实现抽象方法,处理字符串。
AbstractDisplay类
public abstract class AbstractDisplay {
protected abstract void open();
protected abstract void print();
protected abstract void close();
//模板方法
public final void display(){
open();
for (int i = 0; i < 5; i++) {
print();
}
close();
}
}
其中前面3个方法我们交由子类各自处理,我们可以知道的是模板方法display
:
- 调用
open
方法 - 调用5次
print
方法 - 调用
close
方法
子类通过对3个抽象方法的不同实现,可以使程序具有不同的行为。其中模板方法我们使用final
修饰是不想被子类重写。
CharDisplay类
public class CharDisplay extends AbstractDisplay{
private char ch;
public CharDisplay(char ch) {
this.ch = ch;
}
@Override
public void open() {
System.out.print("<<");
}
@Override
public void print() {
System.out.print(ch);
}
@Override
public void close() {
System.out.print(">>");
}
}
我们想要传入的字符像下面这样显示:
<>
我们通过重现父类的抽象方法来改变父类模板方法display
的行为来实现。
StringDisplay类
public class StringDisplay extends AbstractDisplay{
private String s;
private int width;
public StringDisplay(String s){
this.s = s;
this.width = s.length();
}
@Override
public void open() {
printLine();
}
@Override
public void print() {
System.out.print("|");
System.out.print(s);
System.out.println("|");
}
@Override
public void close() {
printLine();
}
private void printLine(){
System.out.print("+");
for (int i = 0; i < width; i++){
System.out.print("-");
}
System.out.println("+");
}
}
我们想要传入的字符串像下面这样显示:
±----------------+
|Hello,World!|
|Hello,World!|
|Hello,World!|
|Hello,World!|
|Hello,World!|
±-----------------+
与CharDisplay类一样,我们通过重写父类的抽象方法来改变模板方法display
来实现上面的效果。
测试
public static void main(String[] args) {
AbstractDisplay charDisplay = new CharDisplay('c');
AbstractDisplay stringDisplay = new StringDisplay("Hello,World!");
System.out.println("展示字符:");
charDisplay.display();
System.out.println();
System.out.println("展示字符串:");
stringDisplay.display();
}
输出:
展示字符:
<<ccccc>>
展示字符串:
+------------+
|Hello,World!|
|Hello,World!|
|Hello,World!|
|Hello,World!|
|Hello,World!|
+------------+
效果符合我们的预期。
我们使用AbstractDisplay
类型的变量来接收两个子类的实例,并分别调用它们的display
方法。Java
的动态绑定机制会在子类中找到抽象方法的实现并使用。
Template Method模式中的角色
AbstractClass
(抽象类):
AbstractClass
角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass
角色负责实现。在示例程序中,由AbstractDisplay
类扮演此角色。
ConcrecteClass
(具体类)
该角色负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass
角色的模板方法中被调用。在示例程序中,由CharDisplay
类和StringDisplay
类扮演此角色。
Template Method
模式的类图:
拓展思路
Template Method模式的好处
这样的设计模式优点是由于父类在模板方法中编写了算法,因此无需在每个子类中再编写算法。
我们也可以不使用这样的方法而是将复制粘贴编写多个ConcreteClass,此时若是出现了BUG的话我们就需要修改多处地方,而使用模板方法则仅仅只需要修改模板方法即可解决问题。
父类和子类之间的协作
在Template Method
模式中,父类和子类是紧密联系、共同工作的。因此,在子类中实现父类中声明的抽象方法时,必须要理解这些抽象方法被调用的时机。在看不到父类的源代码的情况下,想要编写出子类是非常困难的。
父类与子类的一致性
在示例程序中,不论是CharDisplay
的实例还是StringDisplay
的实例,都是先保存在AbstractDisplay
类型的变量中,然后再来调用display方法的。
使用父类类型的变量保存子类实例的优点是,即使没有用instanceof等指定子类的种类,程序也能正常工作。
无论父类类型的变量保存哪个子类的实例,程序都可以正常工作,这样的原则称为里氏替换原则。这是一种通用的继承原则。
相关的设计模式
Factory Method
设计模式
Factory Method
设计模式是将Template Method
模式用于生成实例的一个例子。
Strategy
模式
在Template Method
模式中,可以使用继承改变程序的行为。这是因为Template Method
模式在父类中定义程序行为的框架,在子类中决定具体的处理。
这是一种通用的继承原则。
相关的设计模式
Factory Method
设计模式
Factory Method
设计模式是将Template Method
模式用于生成实例的一个例子。
Strategy
模式
在Template Method
模式中,可以使用继承改变程序的行为。这是因为Template Method
模式在父类中定义程序行为的框架,在子类中决定具体的处理。
而在Strategy
模式中,它可以使用委托改变程序的行为。与Template Method
模式中改变部分程序行为不同的是,Strategy
模式用于替换整个算法。