在看HDA代码的时候发现其中对于归档任务和清理任务的设计用了模板方法的设计模式,详细学习记录了一下。
基本介绍
模板方法定义了一个算法的步骤,并允许子类为其中的一些步骤提供具体的实现方式。这种模式让子类在不需要改变整体结构的情况下,可以重新定义算法的具体步骤。
一般来说,我们在抽象类中定义一个算法的步骤和流程,子类按需重写抽象类中的抽象方法。
具体实现案例
- HDA示例
下面为HDA归档和清理任务的UML类图:
从上图可以很清楚的看出这几个类之间的结构,ArchiveTask
和CleanTask
作为具体的实现类,继承了抽象类AbstractTask
,抽象类又实现了一个ITask
的接口,接口中定义了算法中必须要实现的方法,强制性的定义了算法的必须要实现的方法,具体的任务执行方法execute
和错误处理方法HandleError
就要在两个子类中分别进行实现了,抽象类中提供了三个模板方法分别是isInterrupt
、getSummaryDescription
和getErrorTableLog
,这是三个通用的方法,抽象出来放在父类中。
这里模板方法没有用到钩子方法,钩子方法是在模板方法的父类中定义一个方法,它默认可以不做任何事,子类可以根据实际情况来重写,这样的方法称为“钩子”。
HDA这里两个子类中还有很多相同的方法,可以再进一步抽象到父类中。
- 示例二
抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public abstract class AbstractDisplay {
public abstract void open();
public abstract void print();
public abstract void close();
public final void display() { open(); for (int i = 0; i < 5; i++) { print(); } close(); } }
|
子类:CharDisplay
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class CharDisplay extends AbstractDisplay {
private char ch; public CharDisplay(char ch) { this.ch = ch; } public void open() { System.out.print("<<"); } public void print() { System.out.print(ch); } public void close() { System.out.println(">>"); } }
|
子类:StringDisplay
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
public class StringDisplay extends AbstractDisplay {
private String string;
private int width;
public StringDisplay(String string) { this.string = string; this.width = string.getBytes().length; }
public void open() { printLine(); }
public void print() { System.out.println("|" + string + "|"); }
public void close() { printLine(); }
private void printLine() { System.out.print("+"); for (int i = 0; i < width; i++) { System.out.print("-"); } System.out.println("+");
}
}
|
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class Main {
public static void main(String[] args) {
AbstractDisplay d1 = new CharDisplay('H');
AbstractDisplay d2 = new StringDisplay("Hello, world.");
AbstractDisplay d3 = new StringDisplay("你好,世界。");
d1.display(); d2.display(); d3.display(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <<HHHHH>> +-------------+ |Hello, world.| |Hello, world.| |Hello, world.| |Hello, world.| |Hello, world.| +-------------+
+------------------+ |你好,世界。| |你好,世界。| |你好,世界。| |你好,世界。| |你好,世界。| +------------------+ Process finished with exit code 0
|
总结
- 优点
- 统一了算法,同时也提供了一定的灵活性。父类中的模板方法确保了算法的结构保持不变,同时由子类提供一些具体方法的实现。
- 实现了最大化的代码复用。父类中的模板方法和已实现的某些步骤子类可以直接复用,而如果模板方法需要修改也只要修改一个类即可,对子类的调用来说的透明的,不需要进行修改。