模板方法模式
模板方法模式
模板方法模式(Template Method Pattern)是很重要的一种设计模式,它具体属于行为模式中的一种。
模板方法模式在一个操作中定义了一个算法流程的大致骨架,而这些算法流程骨架中包含的一些步骤被推迟到子类去实现。这样就可以在保证算法既定流程步骤不变的情况下,给与了子类重定义算法表现的机会。
模板方法模式符合面向对象设计的对扩展开放,对修改关闭的原则。
模板方法实际就是利用抽象、继承等概念的一种常见的编程模式,体现了面向对象编程的主要特性。
该模式通常会涉及到一个抽象类,抽象类里面包含一个或多个Template Method,以及几个必须被子类重写的抽象的Primitive Method,其中Template Method调用这些Primitive Method来实现算法的基本流程。另外,抽象类可能还包含一些非抽象的Hook Method,子类可以选择性的重写这些Hook方法以达到扩展功能的目的,但是对于Primitive Method,子类是必须重写的。
实现说明:
- 基于上述说明,在
Java
等面向对象的语言中,可以将其Template Method
声明为final
的方法防止被子类重写,Primitive Method
声明为abstract
方法强制子类重写,而Hook Method
则声明为普通的非抽象方法,默认实现是空。 Primitive Method
的方法(需要被重写)数量尽可能少,否则会给子类带来很多繁琐的实现。- 命令规范,为了和其他的方法区分开来,
Prmitive Methond
通常命名为doXXX()
,这样就暗示这个方法是子类必须要重写的,且会影响整个Template Method
执行效果。
UML类图
UML类图如下:
实例
public abstract class AbstractApplication {
//primitive method
protected abstract Document doCreateDocument();
protected abstract boolean canOpenDocument(String name);
//hook method
public void aboutToOpenDocument(Document doc) {
//do nothing
}
//template method
public final void openDocument(String name) {
//step1
if (!canOpenDocument(name)) {//调用Primitive Method
System.out.println("can not open document!");
return;
}
//step2
Document doc = doCreateDocument();//调用Primitive Method
if (doc != null) {
aboutToOpenDocument(doc);//调用hook方法
doc.open();
doc.doRead();
}
}
}
public abstract class Document {
//primitive method
public abstract void doRead();
protected abstract String getType();
protected abstract String getName();
//template method
public void open() {
System.out.println("is openning document, please waiting ....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(String.format("open document[name=%s, type=%s]", getName(), getType()));
}
}
public class ImageApplication extends AbstractApplication {
private String name = "";
@Override
protected Document doCreateDocument() {
// TODO Auto-generated method stub
return new ImageDocument(name);
}
@Override
protected boolean canOpenDocument(String name) {
// TODO Auto-generated method stub
if (name != null && (name.contains(".jpg") || name.contains(".png") || name.contains(".gif"))) {
this.name = name;
return true;
}
return false;
}
//optional hook method
@Override
public void aboutToOpenDocument(Document doc) {
// TODO Auto-generated method stub
System.out.println("I am hooking aboutToOpenDocument()");
super.aboutToOpenDocument(doc);
}
}
public class ImageDocument extends Document {
private String name = "";
public static final String TYPE = "IMAGE";
public ImageDocument(String name) {
this.name = name;
}
@Override
public void doRead() {
// TODO Auto-generated method stub
System.out.println("I am reading " + this.name + ", type : " + TYPE);
}
@Override
protected String getType() {
// TODO Auto-generated method stub
return TYPE;
}
@Override
protected String getName() {
// TODO Auto-generated method stub
return name;
}
}
public class Main {
public static void main(String[] args) {
AbstractApplication app = new ImageApplication();
app.openDocument("spground.jpg");
}
}
总结
模板方法很重要,其将不变(invariants)的部分封装在模板方法的调用流程中,而将变化(variants)的不拆分为一些基本步骤Primitive Method
以供子类去实现,既对外提供了统一的功能接口,又保证了功能的可扩展性。
模板方法的使用关键在于如何将可变和不可变的部分区分开来,个人认为模板方法模式的使用场景通常是在框架编写方面,编码人员的代码主要是用来供给用户使用或者扩展。
References
- 《设计模式:可复用面向对象软件的基础》