设计模式(四)--模板方法模式
这是一个在许多优秀的开源项目中LZ见的最多的一个设计模式,也是LZ觉得最为优秀的一个设计模式。
模板方法模式,一般是为了统一子类的算法实现步骤,所使用的一种手段或者说是方式。它在父类中定义一系列算法的步骤,而将具体的实现都推迟到子类。
下面LZ给举一个例子,比如我们有一个接口,里面就一个方法,是用来制造一个HTML页面,如下。
public interface PageBuilder{ String buildHtml(); }
这个接口很简单,就是直接制造一个html页面内容,假设不使用模板方法模式,直接让各个子类去直接实现这个接口,那么肯定实现的方式多种多样,而且步骤也乱七八糟,这样不利于维护和扩展。所以我们可以使用模板方法模式,将这个过程给制定好,然后把具体的内容填充交给子类就好,这样这些子类生成的HTML页面就会一致。
基于这个目的,我们定义如下抽象类,去实现这个接口,并且我们定义好步骤。
public abstract class AbstractPageBuilder implements PageBuilder{ private static final String DEFAULT_DOCTYPE = "<!DOCTYPE HTML>"; private StringBuffer stringBuffer = new StringBuffer(); public String bulidHtml() { stringBuffer.append(DEFAULT_DOCTYPE); stringBuffer.append("<html>"); stringBuffer.append("<head>"); appendTitle(stringBuffer); appendMeta(stringBuffer); appendLink(stringBuffer); appendScript(stringBuffer); stringBuffer.append("</head>"); appendBody(stringBuffer); stringBuffer.append("</html>"); return stringBuffer.toString(); } //供子类选择性覆盖 protected void appendMeta(StringBuffer stringBuffer){ } //供子类选择性覆盖 protected void appendLink(StringBuffer stringBuffer){ } //供子类选择性覆盖 protected void appendScript(StringBuffer stringBuffer){ } //模板方法 添加标题 protected abstract void appendTitle(StringBuffer stringBuffer); //模板方法 添加body protected abstract void appendBody(StringBuffer stringBuffer); }
子类只要实现两个模板方法,就可以成功创建html页面。
public class MyPageBuilder extends AbstractPageBuilder{ protected void appendMeta(StringBuffer stringBuffer) { stringBuffer.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"); } protected void appendTitle(StringBuffer stringBuffer) { stringBuffer.append("<title>你好</title>"); } protected void appendBody(StringBuffer stringBuffer) { stringBuffer.append("<body>你好,世界!</body>"); } public static void main(String[] args) { PageBuilder pageBuilder = new MyPageBuilder(); System.out.println(pageBuilder.bulidHtml()); } }
meta,link和script是留给子类覆盖的,当然子类可以选择不覆盖,那么生成的HTML就没有meta,link和script这三种标签,如果想有的话,就可以覆盖其中任意一个。
为了不强制子类实现不必要的抽象方法,但又不剥夺子类自由选择的权利,我们在父类提供一个默认的空实现,来让子类自由选择是否要覆盖掉这些方法。