模板方法模式

模板方法模式

模板方法模式(Template Method Pattern)是很重要的一种设计模式,它具体属于行为模式中的一种。

模板方法模式在一个操作中定义了一个算法流程的大致骨架,而这些算法流程骨架中包含的一些步骤被推迟到子类去实现。这样就可以在保证算法既定流程步骤不变的情况下,给与了子类重定义算法表现的机会。

模板方法模式符合面向对象设计的对扩展开放,对修改关闭的原则。

模板方法实际就是利用抽象、继承等概念的一种常见的编程模式,体现了面向对象编程的主要特性。

该模式通常会涉及到一个抽象类,抽象类里面包含一个或多个Template Method,以及几个必须被子类重写抽象Primitive Method,其中Template Method调用这些Primitive Method来实现算法的基本流程。另外,抽象类可能还包含一些非抽象的Hook Method,子类可以选择性的重写这些Hook方法以达到扩展功能的目的,但是对于Primitive Method,子类是必须重写的。

实现说明

  1. 基于上述说明,在Java等面向对象的语言中,可以将其Template Method声明为final的方法防止被子类重写,Primitive Method声明为abstract方法强制子类重写,而Hook Method则声明为普通的非抽象方法,默认实现是空。
  2. Primitive Method的方法(需要被重写)数量尽可能少,否则会给子类带来很多繁琐的实现。
  3. 命令规范,为了和其他的方法区分开来,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

  1. 《设计模式:可复用面向对象软件的基础》
原文地址:https://www.cnblogs.com/Spground/p/9567889.html