Template Method——模板方法模式
什么是模板方法模式
在父类中定义处理流程的框架,在子类中实现具体处理的模式就称为模板方法模式,模板方法通过把不变的行为移到父类,去除子类中的重复代码,提高代码复用率。
如果要完成在某一些细节层次一致的一个过程或一系列步骤,旦其个别步骤在更详细的层次上的实现可能不同时,可以考虑用模板方法模式。
模板方法模式中的角色构成
模板方法模式类图
- AbstractClass (抽象类)
AbstractClass角色负责实现模板方法以及声明模板方法中所使用到的抽象方法,这些抽象方法由子类ConcreteClass角色负责实现;模板方法作为具体方法,会给出一个顶级逻辑骨架,逻辑的组成步骤在相应的抽象方法中,会在子类中具体实现。在顶级逻辑中也可能调用一些具体方法。
注意:模板方法不应被重写,所以应使用final修饰符 - ConcreteClass (具体类)
ConcreteClass角色负责具体实现AbstractClass角色中定义的抽象方法,具体类中实现的方法将会在抽象类的模板方法中被调用;每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。
示例程序
类组成表:
类名 | 说明 |
---|---|
AbstractDisplay | 包含open(),print(),close()三个抽象方法及模板方法display() |
CharDisplay | 继承了AbstractDisplay类,实现了open(), print(), close()方法 |
StringDisplay | 继承了AbstractDispaly类,实现了open(), print(), close() 方法 |
TemplateMain | 测试类 |
示例程序类图
AbstractDisplay类:
AbstractDispaly类中有四个方法,具体实现的只有display()方法,其中open()、print()、 close()为抽象方法,方法的实际处理交给了继承的子类。
package com.example.templatemethod;
public abstract class AbstractDisplay {
public abstract void open(); //交给子类去实现的抽象方法(1)open()
public abstract void print(); //交给子类去实现的抽象方法(2)print()
public abstract void close(); //交给子类去实现的抽象方法(3)close()
public final void display() { //本抽象类中实现的display()方法
open();
for (int i = 0; i < 5; i++) {
print();
}
close();
}
}
CharDisplay类:
AbstractDisplay类的一个子类,具体了实现父类中的三个抽象方法
package com.example.templatemethod;
public class CharDisplay extends AbstractDisplay{
private char cd;
public CharDisplay(char cd) {
this.cd = cd;
}
@Override
public void open() {
System.out.print("<<");
}
@Override
public void print() {
System.out.print(cd); //显示构造函数接收的一个字符
}
@Override
public void close() {
System.out.println(">>");
}
}
StringDisplay类:
AbstractDisplay类的另一个子类,实现了父类中的三个抽象方法,其具体实现与CharDisplay有所不同
package com.example.templatemethod;
public class StringDisplay extends AbstractDisplay{
private String string; //需要显示的字符串
private int width; //以字节为单位计算出的字符串长度
public StringDisplay(String string) {
this.string = string;
this.width = string.getBytes().length;
}
@Override
public void open() {
printLine();
}
@Override
public void print() {
System.out.println("|" + string + "|"); //显示构造函数接受的字符串,并在前后加上"|"
}
@Override
public void close() {
printLine();
}
private void printLine() {
for (int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.println("+");
}
}
TempalteMain类:
测试类
package com.example.templatemethod;
public class TemplateMain {
public static void main(String[] args) {
AbstractDisplay ad1 = new CharDisplay('Q');
AbstractDisplay ad2 = new StringDisplay("hello,world");
AbstractDisplay ad3 = new StringDisplay("String3");
ad1.display();
ad2.display();
ad3.display();
}
}
执行结果:
<<QQQQQ>>
-----------+
|hello,world|
|hello,world|
|hello,world|
|hello,world|
|hello,world|
-----------+
-------+
|String3|
|String3|
|String3|
|String3|
|String3|
-------+
拓展延伸
类的层次
通常在理解类的层次时,容易站在子类的角度进行思考:
- 在子类中可以使用父类中的哪些方法
- 在子类中可以增加什么方法以实现新的功能
- 在子类中重写父类的方法可以改变哪些程序行为
转换角度,站在父类的角度进行思考,通过声明抽象方法,将方法的具体实现交给子类。也就是说子类具有实现在父类中所声明的抽象方法的责任,这种责任被称为“子类责任”(subclass reponsibility)
InputStream中的模板方法
java.io.InputStream中有一个抽象方法read(),它会被InputStream的模板方法read(byte b[], int off, int len) 循环调用。InputStream的子类负责具体实现"读取一个字节"的操作,而InputStream中定义的模板方法只负责"将指定数量的字节读取到数组中的指定位置"的操作。
public abstract int read() throws IOException;
public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
画蛇添足
尝试将抽象类改写成接口,将其中的模板方法改写成Java 8加入的接口默认方法
IAbstractDisplay接口:
包含三个抽象方法open()、print()、close(),一个默认方法disPlay()替换原抽象类中的模板方法disPlay()
package com.example.templatemethod.interfaceImpl;
public interface IAbstractDisplay {
void open();
void print();
void close();
default void disPlay() {
open();
for (int i = 0; i < 5; i++) {
print();
}
close();
}
}
CharDisPlayImpl类:
IAbstractDisplay接口的一个实现类,具体实现了接口当中的三个抽象方法
package com.example.templatemethod.interfaceImpl;
public class CharDisplayImpl implements IAbstractDisplay{
private char cd;
public CharDisplayImpl(char cd) {this.cd = cd;}
@Override
public void open() {
System.out.print("<<");
}
@Override
public void print() {
System.out.print(cd);
}
@Override
public void close() {
System.out.println(">>");
}
}
StringDisplayImpl类:
IAbstractDisplay接口的另一个实现类,实现了接口当中的三个抽象方法,与CharDisplayImpl的具体实现不同
InterfaceImplMain类:
测试类
package com.example.templatemethod.interfaceImpl;
public class InterfaceImplMain {
public static void main(String[] args) {
IAbstractDisplay ad1 = new CharDisplayImpl('Q');
IAbstractDisplay ad2 = new StringDisplayImpl("hello,world");
IAbstractDisplay ad3 = new StringDisplayImpl("String3");
ad1.disPlay();
ad2.disPlay();
ad3.disPlay();
}
}
执行结果:
<<QQQQQ>>
-----------+
|hello,world|
|hello,world|
|hello,world|
|hello,world|
|hello,world|
-----------+
-------+
|String3|
|String3|
|String3|
|String3|
|String3|
-------+