1. 需求矛盾
l 同一个父类的几个子类在实现某些功能时具有大多数共同点,但各个子类之间还有不同点。
l 比如三个儿子从事不同的工作,都会赚钱,都知道孝顺父母,但每个人赚钱的方式不同。
思考:
1) 三个儿子的共同点是什么?
2) 三个儿子的相异点是什么?
3) 如何设计可以使三个儿子同时具有共同点,而又能保证每个人的不同点?
2. 是什么——定义
定义一个模板结构,将具体内容延迟到子类去实现。
3. 为什么——特点
1) 提高代码的复用性
2) 实现反向控制(子类决定功能)
4. 什么时候用——适用性
1) 一些方法通用,却在每一个子类都重新写了这一方法。
2) 有多个子类共有的方法,且逻辑相同。
3) 重要的、复杂的方法,可以考虑作为模板方法。
5. UML图
6. 怎么用——使用方法
需求:
抄书与印书
以前没有印刷技术的时候,老师念,学生抄,抄完再背。
存在的问题:万一学生抄错题目了,找书都找不到了!
6.1 没有任何原则的初始设计
public class Demo1 { public static void main(String[] args) { new Book1().show(); new Book2().show(); new Book3().show(); }}class Book1 { public void show() { System.out.println("学生1抄的书——题目。。。"); System.out.println("学生1抄的书——正文。。。"); System.out.println("学生1抄的书——封皮。。。"); }}class Book2 { public void show() { System.out.println("学生2抄的书——题目。。。"); System.out.println("学生2抄的书——正文。。。"); System.out.println("学生2抄的书——封皮。。。"); }}class Book3 { public void show() { System.out.println("学生3吵的书——题日。。。"); System.out.println("学生3吵的书——正文(没抄完)。。。"); System.out.println("学生3吵的书——封皮。。。"); }}
因为学生3没有抄完(而且抄的题目还写错了。。。),以至于之后要背书的时候怎么也背不对了。。。
6.2 抽取公共代码,封装方法
为了避免上面连题目都能抄错,需要把相同的代码提取出来:
abstract class AbstractBook { public void showTitle() { System.out.println("题目。。。"); } public void showFace() { System.out.println("封皮。。。"); }}class Book4 extends AbstractBook { public void show() { showTitle(); System.out.println("学生4抄的书——正文。。。"); showFace(); }}class Book5 extends AbstractBook { public void show() { showTitle(); System.out.println("学生5抄的书——正文。。。"); showFace(); }}
但这样仍存在问题:每个子类需要调用抄题目、封皮的方法,万一忘了呢。。。
6.3 应用模板方法模式
将子类需要抄的正文抽取成一个方法,父类单独写一个模板方法,用于抄题目和封皮:
class AbstractBook { public void showIncomplateBook() { System.out.println("题目。。。"); function(); System.out.println("封皮。。。"); } public void function(){ }}class Book6 extends AbstractBook { @Override public void function() { System.out.println("正文(抄完了)。。。"); }}
但这样还是会出现问题:万一忘了抄正文呢。。。
而且,父类的function里摆着个空壳,也不像回事吧。。。
这时就需要使用一套机制,强制子类抄!————抽象方法
abstract class AbstractBook { public void showIncomplateBook() { System.out.println("题目。。。"); function(); System.out.println("封皮。。。"); } public abstract void function();}class Book7 extends AbstractBook { @Override public void function() { System.out.println("被逼着抄的正文。。。"); }}
加入抽象方法后,子类不得不重写父类的未实现方法,否则无法编译通过。
这套思想可以使用在SSH项目中的dao层的通用抽取:
BaseDaoImpl负责实现全部查询和条件查询的框架,但具体的条件查询,要通过每个模块自己实现,所以这里需要设置模板方法(查全部,返回查询结果)和抽象方法(自定义查询条件)。
模拟代码(跑不动哦,别打我,手动滑稽)
public abstract class BaseDaoImpl { public Object getAll(Object o) { StringBuilder sb = new StringBuilder("select * from tbl_???"); doQuery(sb, o); return sb.toString(); } public abstract void doQuery(StringBuilder sb, Object o);}public class DepartmentDaoImpl extends BaseDaoImpl { @Override public void doQuery(StringBuilder sb, Object o) { /*DepartmentQueryModel dqm = ()o; if (condition) { }*/ }}