模板方法模式:定义一个操作中的算法的骨架,将一些步骤延迟到之类中。模板方法使得之类可以不改变一个算法的结构便可以重定义该算法的某些特定步骤。
在父类中定义一系列算法的步骤,而将具体的实现都推迟到子类。
模板模式就是通过把不变的行为搬迁到超类,去除之类中重复代码来体现它的优势。
最典型的就是一个接口,一个抽象的父类,父类中会有一些抽象的方法,在之类中去实现。
接口:
public interface PageBuilder { String bulidHtml(); }
这个接口很简单,就是直接制造一个Html页面的内容,假设我们不使用模板方法模式,直接让各个子类去直接实现这个接口,那么肯定实现的方式千奇百怪,而且步骤也乱七八糟的,这样实在不利于维护和扩展。所以我们可以使用模板方法模式,将这个过程给制定好,然后把具体的内容填充交给子类就好,这样这些子类生成的HTML页面就会非常一致。
基于这个目的,我们定义如下抽象类,去实现这个接口,并且我们定义好步骤。
public abstract class AbstractPageBuilder implements PageBuilder{ private StringBuffer stringBuffer = new StringBuffer(); public String bulidHtml() { //首先加入doctype,因为都是html页面,所以我们父类不需要推迟给子类实现,直接在父类实现 stringBuffer.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); //页面下面就是成对的一个HTML标签,我们也在父类加入,不需要给子类实现 stringBuffer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">"); //下面就应该是head标签里的内容了,这个我们父类做不了主了,推迟到子类实现,所以我们定义一个抽象方法,让子类必须实现 appendHead(stringBuffer); //下面是body的内容了,我们父类依然无法做主,仍然推迟到子类实现 appendBody(stringBuffer); //html标签的关闭 stringBuffer.append("</html>"); return stringBuffer.toString(); } //第一个模板方法 protected abstract void appendHead(StringBuffer stringBuffer); //第二个模板方法 protected abstract void appendBody(StringBuffer stringBuffer); }
继承类:
public class MyPageBuilder extends AbstractPageBuilder{ @Override protected void appendHead(StringBuffer stringBuffer) { stringBuffer.append("<head><title>你好</title></head>"); } @Override protected void appendBody(StringBuffer stringBuffer) { stringBuffer.append("<body><h1>你好,世界!</h1></body>"); } public static void main(String[] args) { PageBuilder pageBuilder = new MyPageBuilder(); System.out.println(pageBuilder.bulidHtml()); } }
对于模板方法模式,父类提供的构建步骤和顺序或者算法骨架,通常是不希望甚至是不允许子类去覆盖的,所以在某些场景中,可以直接将父类中提供骨架的方法声明为final类型。
所以为了不强制子类实现不必要的抽象方法,但又不剥夺子类自由选择的权利,我们在父类提供一个默认的空实现,来让子类自由选择是否要覆盖掉这些方法。
说到模板方法模式,我们JDK当中有一个类与它还有一个不得不说的故事,那就是类加载器(会写在其他的分类中)