模板方法模式,定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
AbstractClass:实现一个模板方法,定义了算法的骨架,具体子类将重定义PrimitiveOperation以实现一个算法的步骤。AbstractClass其实就是一个抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体的方法。它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
ConcreteClasses:实现PrimitiveOperation以完成算法与特定子类相关的步骤。ConcreteClass实现父类所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
根据上面的结构图,我们可以用代码来进行实现
AbstractClass(抽象模板):
package com.jxs.templateMethod;
/**
* Created by jiangxs on 2018/5/7.
*/
public abstract class AbstractClass {
public abstract void primitiveOperation1();
public abstract void primitiveOperation2();
// 模板方法,给出了逻辑的骨架
// 而逻辑的组成是一些相应的抽象操作,他们都推迟到子类实现
public void templateMethod() {
primitiveOperation1();
primitiveOperation2();
System.out.println("");
}
}
ConcreteClass(实现父类所定义的一个或多个抽象方法)
ConcreteClassA:
package com.jxs.templateMethod;
/**
* Created by jiangxs on 2018/5/7.
*/
public class ConcreteClassA extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("具体类A方法1实现");
}
@Override
public void primitiveOperation2() {
System.out.println("具体类A方法2实现");
}
}
ConcreteClassB:
package com.jxs.templateMethod;
/**
* Created by jiangxs on 2018/5/7.
*/
public class ConcreteClassB extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("具体类B方法1实现");
}
@Override
public void primitiveOperation2() {
System.out.println("具体类B方法2实现");
}
}
客户端:
package com.jxs.templateMethod;
/**
* Created by jiangxs on 2018/5/7.
*/
public class Client {
public static void main(String[] args) {
AbstractClass c;
c = new ConcreteClassA();
c.templateMethod();
c = new ConcreteClassB();
c.templateMethod();
}
}
运行结果:
具体类A方法1实现
具体类A方法2实现
具体类B方法1实现
具体类B方法2实现
Process finished with exit code 0
假设老师在黑板上出了一套题,让学生A和学生B将黑板上的题目抄下来做好了交上去给老师进行批改。但是学生很有可能因为一不小心将题目抄错了而将答案选错,这样就会让老师误以为这位同学没有掌握。并且,当老师改了黑板上的题目后,那么这两个同学也要跟着去修改,非常的麻烦。那么怎样解决这些问题呢?
其实老师将题目出好制作一份试卷,然后将这个试卷打印多份发给学生,这样,学生拿到的题目就是一样的了,并且学生只需要填写答案,不需要抄题,也省去了工作量。这个就是模板方法模式的体现。其中试卷就相当于是一个模板,而多个学生做这个试卷就相当于多个ConcreteClass去实现试卷这个模板中的方法。
上面的例子可以用结构图表示:
将该例子用代码实现:
TestPaper类(AbstractClass)
package com.jxs.templateMethodExample;
/**
* Created by jiangxs on 2018/5/7.
*/
public class TestPaper {
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖," +
"炼成倚天剑、屠龙刀的玄铁可能是[ ]\n" +
"a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维");
System.out.println("答案: " + answer1());
}
public void testQuestion2() {
System.out.println("杨过、程英、陆无双铲除了情花,造成[ ]\n" +
"a.使这种植物不再伤人 b.使一种珍惜物种灭绝\n" +
"c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化");
System.out.println("答案: " + answer2());
}
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙呕吐不止," +
"如果你是大夫,会给他们开什么药[ ]\n" +
"a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.大量生牛奶");
System.out.println("答案: " + answer3());
}
public String answer1() {
return null;
}
public String answer2() {
return null;
}
public String answer3() {
return null;
}
}
TestPaperA类(ConcreteClass)
package com.jxs.templateMethodExample;
/**
* Created by jiangxs on 2018/5/7.
*
* 学生甲抄的试卷
*/
public class TestPaperA extends TestPaper {
@Override
public String answer1() {
return "d";
}
@Override
public String answer2() {
return "c";
}
@Override
public String answer3() {
return "c";
}
}
TestPaperB(ConcreteClass):
package com.jxs.templateMethodExample;
/**
* Created by jiangxs on 2018/5/7.
*
* 学生乙抄的试卷
*/
public class TestPaperB extends TestPaper {
@Override
public String answer1() {
return "a";
}
@Override
public String answer2() {
return "a";
}
@Override
public String answer3() {
return "a";
}
}
客户端:
package com.jxs.templateMethodExample;
/**
* Created by jiangxs on 2018/5/7.
*/
public class TestPaperClient {
public static void main(String[] args) {
System.out.println("学生甲抄的试卷:");
TestPaperA studentA = new TestPaperA();
studentA.testQuestion1();
studentA.testQuestion2();
studentA.testQuestion3();
System.out.println();
System.out.println("学生乙抄的试卷:");
TestPaperB studentB = new TestPaperB();
studentB.testQuestion1();
studentB.testQuestion2();
studentB.testQuestion3();
}
}
运行结果:
学生甲做的试卷:
杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ]
a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案: d
杨过、程英、陆无双铲除了情花,造成[ ]
a.使这种植物不再伤人 b.使一种珍惜物种灭绝
c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化
答案: c
蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药[ ]
a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.大量生牛奶
答案: c
学生乙做的试卷:
杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ]
a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案: a
杨过、程英、陆无双铲除了情花,造成[ ]
a.使这种植物不再伤人 b.使一种珍惜物种灭绝
c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化
答案: a
蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药[ ]
a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.大量生牛奶
答案: a
Process finished with exit code 0
①模板方法模式通过把不变的行为搬移到父类,去除了子类中的重复代码。
②子类实现算法的某些细节,有助于算法的扩展。
③通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。
①多个子类有共有的方法,并且逻辑基本相同。
②重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
③重构时,模板方法是一个经常使用的方法,把相同的代码抽取到父类中,然后通过构造函数约束其行为。
注:以上代码均可在github上进行下载:https://github.com/xsongj/designPattern
参考:《大话设计模式》