模板方法的概念
模板方法模式是定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
通俗点的理解说就是完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
模板模式的结构
抽象类模板
实现了模板方法,定义了算法的骨架。
具体类模板
实现抽象类中的抽象方法,即不同的对象的具体实现细节。
代码实例
小A和小B去参加考试的故事。
这是A的问卷:
public class TestPageA {
// 试题1
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
System.out.println("答案:b");
}
// 试题2
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println(" 答案: a");
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: c");
}
}
这是B的问卷:
public class TestPageB {
// 试题1
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
System.out.println("答案:a");
}
// 试题2
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println(" 答案: b");
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: a");
}
}
测试类:
public class Test {
public static void main(String[] args) {
System.out.println("小A的试卷");
TestPageA testPageA = new TestPageA();
testPageA.testQuestion1();
testPageA.testQuestion2();
testPageA.testQuestion3();
System.out.println("小B的试卷");
TestPageB testPageB = new TestPageB();
testPageB.testQuestion1();
testPageB.testQuestion2();
testPageB.testQuestion3();
}
}
通过上面的代码我们可以看出小A和小B的试卷除了答案不相同,没什么不一样,这样写又容易出错,又难以维护。如果老师要更改题目,那么两个人都要更改代码。接下来我们用模板方法的方式解决。
模板方法模式
下面使用的是模板方法模式
抽象类角色:
public abstract class TestPage {
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
if (isAnswer()) {
System.out.println("答案:"+answer1());
} else {
System.out.println("答案:同学未填写答案");
}
}
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println("答案:"+answer2());
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: "+answer3());
}
// 回答问题方法1
public abstract String answer1();
// 回答问题方法2
public abstract String answer2();
// 回答问题方法3
public abstract String answer3();
// 是否填写答案
public abstract boolean isAnswer () ;
}
具体类(小A):
public class TestPageA extends TestPage{
@Override
public String answer1() {
return "b";
}
@Override
public String answer2() {
return "a";
}
@Override
public String answer3() {
return "c";
}
@Override
public boolean isAnswer() {
return false;
}
}
具体类(小B)
public class TestPageB extends TestPage {
@Override
public String answer1() {
return "a";
}
@Override
public String answer2() {
return "b";
}
@Override
public String answer3() {
return "a";
}
@Override
public boolean isAnswer() {
return true;
}
}
测试类
public class Test {
public static void main(String[] args) {
System.out.println("---------小A的试卷--------");
TestPage testPageA = new TestPageA();
testPageA.testQuestion1();
testPageA.testQuestion2();
testPageA.testQuestion3();
System.out.println("---------小B的试卷---------");
TestPage testPageB = new TestPageB();
testPageB.testQuestion1();
testPageB.testQuestion2();
testPageB.testQuestion3();
}
}
此时更多的学生来回答试卷,只不过在试卷答案模板上填写选择题的答案,这是每个人唯一的同。在代码中添加的isAnswer方法在模板方法模式也称钩子方法,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
特点
模板方法模式是通过把不变的行为搬移到抽象类,去除子类中的重复代码。
优缺点
优点
模板方法模式在一个类中定义算法,而由它的子类实现细节的处理。
模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,,我们找你”通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点
每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
Servlet中的模板方法模式
Servlet(Server Applet)是Java Servlet的简称,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。在每一个 Servlet 都必须要实现 Servlet 接口,GenericServlet 是个通用的、不特定于任何协议的Servlet,它实现了 Servlet 接口,而 HttpServlet 继承于 GenericServlet,实现了 Servlet 接口,为 Servlet 接口提供了处理HTTP协议的通用实现,所以我们定义的 Servlet 只需要继承 HttpServlet 即可。
在 HttpServlet 的 service 方法中,首先获得到请求的方法名,然后根据方法名调用对应的 doXXX 方法,比如说请求方法为GET,那么就去调用 doGet 方法;请求方法为POST,那么就去调用 doPost 方法,HttpServlet 相当于定义了一套处理 HTTP 请求的模板;service 方法为模板方法,定义了处理HTTP请求的基本流程;doXXX 等方法为基本方法,根据请求方法做相应的处理,子类可重写这些方法;HttpServletRequest 中的Method则起到钩子方法的作用.
适用场景
对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。
子类中公共的行为提取出来并集中到一个抽象类中以避免代码重复。
通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。