一,概念
模板方法模式:
定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。
模板方法模式的关键是在抽象类中定义一个算法的框架,即模板方法,该方法将若干个方法集成到一个方法中。
【案例程序1“熊出没”中光头强伐树】
国产动画片“熊出没”中,光头强伐树分为准备、出发、伐树、运输、贩卖等5个主要过程。剧情不同具体的实现内容不同,例如在某个剧情中开车运输木头、而在另一个剧情中采用3轮车运输木头等等。
国产动画片“熊出没”中,光头强伐树分为准备、出发、伐树、运输、贩卖等5个主要过程。 剧情不同具休的实现的内容不同,例如在某个剧情中采用开车运输、在另一个剧情中采用三轮车运输等等
传统解决方法:
package TemplateMethod.negative;
//剧情1中
//1,准备砍树行头
//2,出发
//3,用-------电锯-------砍树
//4,用-------卡车-------运送木材
//5,不需要贩卖木材(可能需要,也可能不需要)
//6,吃午饭
class LoggerQiang{
//每个打印都是一个方法,以下是为了简化代码
public void workOn(){
System.out.println("光头强准备砍树");
System.out.println("光头强出发了");
System.out.println("光头强用电锯砍树");
System.out.println("光头强开卡车运输木材");
System.out.println("光头强不需要贩卖木材");
System.out.println("光头强吃午餐");
}
}
//剧情2中
//1,准备砍树行头
//2,出发
//3,用-------斧头-------砍树------与剧情1不同
//4,用-------三轮车-------运送木材------与剧情1不同
//5,贩卖木材(可能需要,也可能不需要)------与剧情1不同
//6,吃午饭
class LoggerQiang2{
//每个打印都是一个方法,以下是为了简化代码
public void workOn(){
System.out.println("光头强准备砍树");
System.out.println("光头强出发了");
System.out.println("光头强用斧头砍树");
System.out.println("光头强开三轮车运输木材");
System.out.println("光头强需要贩卖木材");
System.out.println("光头强吃午餐");
}
}
//客户端
public class DemoN{
public static void main(String[] args) {
System.out.println("-----剧情1------");
new LoggerQiang().workOn();
System.out.println("-----剧情2------");
new LoggerQiang2().workOn();
}
}
//缺点
//虽然光头强伐木过程相同,但是要采取不同的具体方式
//上面程序不能适应剧情需要,当剧情改变时,需要重写
// LoggerQiang类的WorkOn方法,违反开闭原则
采用模板方法设计
package TemplateMethod.positive;
//======模板方法模式正例
//采用模板方法模式
//1,采用抽象模板方法
abstract class LoggerTemplate{
protected void ready(){//基本方法
System.out.println("光头强在准备......");
}
protected void setout(){//基本方法
System.out.println("光头强出发了......");
}
protected void sellToOther(){//基本方法
System.out.println("光头强自己卖木头......");
}
protected void sellToLi(){//基本方法
System.out.println("光头强把木头给李老板卖......");
}
protected boolean isSell(){//钩子方法,决定卖木头的方法,需要在子类覆写
return true;
}
protected void eat(){//基本方法
System.out.println("光头强在吃东西");
}
protected abstract void cutTree();//基本方法,不能确定用什么工具伐树
protected abstract void transport();//基本方法,不能确定用什么工具运送木材
public final void workOn(){//模板方法,算法骨架
this.ready();
this.setout();
this.cutTree();
this.transport();
if(isSell())//为真,自己卖
this.sellToOther();
else//为假,给李老板卖
this.sellToLi();
this.eat();
}
}
//2,具体模板类
//剧情1中
class LoggerQiang extends LoggerTemplate{
@Override
protected void cutTree() {
System.out.println("用电锯砍树......");
}
@Override
protected void transport() {
System.out.println("用卡车运送木材");
}
}
//剧情2中
class LoggerQiang2 extends LoggerTemplate{
@Override
protected void cutTree() {
System.out.println("用斧头砍树");
}
@Override
protected void transport() {
System.out.println("用三轮车运送木头");
}
//重写钩子方法,光头强给李老板卖
public boolean isSell(){
return false;
}
}
//客户端
public class DemoPositive{
public static void main(String[] args) {
System.out.println("-----剧情1------");
LoggerTemplate a=new LoggerQiang();
a.workOn();
System.out.println("-----剧情2------");
LoggerTemplate b=new LoggerQiang2();
b.workOn();
}
}
//依赖抽象,符合依赖倒置原则
二,模式结构
模板方法模式结构中只有2个角色:
(1)抽象模板(Abstract Template)。抽象模板是一个抽象类,该类中定义了若干方法(包括抽象和非抽象方法),该类中还定义了一个模板方法(Template Method)用于定义一个算法的框架。模板方法可以调用该类中的各种方法(抽象和非抽象方法),即模板方法定义了算法框架。
“钩子”方法也称为“挂钩”方法,一般命名规则是isXXXO或hasXXX(,返回类型为boolean类型。
作用:通过钩子方法来确定模板方法中的某一步骤(方法)是否可以被执行。
(2)具体模板(ConcreteTemplate)。具体模板是抽象模板的子类,用于实现在父类中定义的抽象方法,也可以覆盖在父类中已经定义的非抽象方法。
三,UML类图
三种方法:
(1)模板方法(TemplateMethod)
(2)基本方法(Primitive Method)
抽象方法(Abstract Method)
具体方法(Concrete Method)
(3)钩子方法(Hook Method)
四,模式优点
(1)在抽象模板类中形式化地定义一个算法的框架—执行次序(即模板方法),而由它的子类来实现细节处理,因此子类更改处理算法(细节)时并不会改变模板方法的执行次序。
(2)该模式提取类库中的公共行为并定义在父类中,通过其子类来实现不同的行为,鼓励在设计中恰当使用继承来实现代码复用。
(3)通过子类覆盖父类的钩子方法的方式来决定模板方法中的某特定步骤是否需要执行,实现一种反向控制结构。
(4)通过子类覆盖父类基本方法的方式,不同子类能够提供基本方法的不同实现,在更换和增加新的子类时不需要修改源代码,符合单一职责原则和开闭原则。
五,模式缺点
如果抽象模板类中可变的基本方法比较多,需要为每一个可变的基本方法定义一个子类(具体模板类),将会增加比较多的子类,系统变得庞大,设计也需要更加抽象,增加了系统的理解难度和设计难度。
六,应用场景
(1)如果若干个子类有逻辑相同的公共行为,那么把公共行为提取出来定义在父类中,以避免代码重复;
(2)需要通过子类决定父类某个算法(模板方法)中的某个步骤是否需要执行,实现子类对父类的反向控制(钩子方法);
(3)在父类中实现一个算法的不变部分,子类实现算法中的可变行为。
七,应用举例
AbstractList是抽象模板类,而ArrayList是具体子类。
AbstractList定义了add方法,而子类ArrayList覆写了这个方法。
【案例程序2数据库的操作】
利用JDBC操作数据库分为4步:
(1)加载数据库驱动程序;
(2)连接数据库服务器;
(3)创建SQL语句;
(4)操作结果。
利用模板方法模式设计JDBC操作数据库。