1. 模版方法模式概述
模版方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。模版方法模式通过把固定不变的部分抽取到父类中,将可变的部分留给子类来实现,从而实现代码复用和扩展。
在模版方法模式中,定义了一个抽象类,该抽象类包含一个模版方法(Template Method),该方法定义了算法的骨架,其中包含了一些具体的步骤,而这些步骤的具体实现由子类来完成。抽象类中的模版方法可以定义为 final,确保不会被子类重写,以保持算法的一致性。
模版方法模式的核心思想是将共同的代码逻辑抽取到父类中,而将不同的实现细节交给子类来实现。这样可以避免代码的重复,提高代码的复用性和可维护性。同时,模版方法模式也提供了一种扩展算法的方式,通过继承并重写父类的方法来实现新的算法。
2. 模版方法模式结构
模版方法模式包含以下几个核心组件:
- 抽象类(Abstract Class):
- 定义一个模版方法(Template Method),该方法作为算法的骨架,包含一系列的步骤。
- 声明抽象方法或可选的钩子方法,供子类实现或重写。
- 定义模版方法的具体实现逻辑,包含调用步骤的顺序和结构。
- 具体类(Concrete Class):
- 继承抽象类,实现抽象方法或重写钩子方法。
- 实现具体的步骤,完成算法的具体细节。
3. 模版方法模式示例
豆浆制作问题
编写制作豆浆的程序,说明如下:
- 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
- 通过添加不同的配料,可以制作出不同口味的豆浆
- 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
- 请使用 模板方法模式 完成 (说明:因为模板方法模式,比较简单,很容易就
想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式 )
抽象类:
public abstract class SoyaMilk {
/**
* 模板方法:
* final 不让子类覆盖
*/
final void make() {
select();
if(customerWantCondiments()) {
add();
}
soak();
beat();
}
// 钩子方法,决定是否需要添加配料
boolean customerWantCondiments() {
return true;
}
void select() {
System.out.println("第一步:选择新鲜黄豆");
}
// 抽象类(具体由子类完成)
abstract void add();
void soak() {
System.out.println("第三步:黄豆和配料浸泡~~~需要三个小时");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机打碎");
}
}
具体实现类1:
public class BlackBeanSoyaMilk extends SoyaMilk{
@Override
void add() {
System.out.println("第二步:加入黑豆");
}
}
具体实现类2:
public class PennutSoyaMilk extends SoyaMilk{
@Override
void add() {
System.out.println("第二步:加入花生");
}
}
具体实现类3:
public class PureSoyaMilk extends SoyaMilk{
@Override
void add() {
//System.out.println("第二步:不加配料");
}
@Override
boolean customerWantCondiments() {
return false;
}
}
测试:
public class TemplateTest {
public static void main(String[] args) {
System.out.println("制作黑豆豆浆");
BlackBeanSoyaMilk blackBeanSoyaMilk = new BlackBeanSoyaMilk();
blackBeanSoyaMilk.make();
System.out.println("制作花生豆浆");
PennutSoyaMilk pennutSoyaMilk = new PennutSoyaMilk();
pennutSoyaMilk.make();
System.out.println("制作原味豆浆");
PureSoyaMilk pureSoyaMilk = new PureSoyaMilk();
pureSoyaMilk.make();
}
}
结果:
4. 模版方法模式与其他设计模式的关系
- 策略模式(Strategy Pattern):在策略模式中,算法是通过组合而不是继承的方式实现的。每个具体策略类都实现了一个算法,并通过接口暴露给客户端使用。而在模版方法模式中,算法是通过继承和重写的方式实现的,具体步骤由子类实现。两种模式都可以实现在运行时动态选择不同的算法。
- 工厂方法模式(Factory Method Pattern):在工厂方法模式中,工厂类定义了一个创建对象的接口,由子类决定具体创建哪个对象。而在模版方法模式中,抽象类定义了一个模版方法,由子类实现具体的步骤。两种模式都涉及到定义抽象的行为,但工厂方法模式侧重于创建对象,而模版方法模式侧重于定义算法的流程。
- 命令模式(Command Pattern):在命令模式中,将请求封装成一个对象,从而使得可以参数化客户端请求。而在模版方法模式中,将算法封装在一个模版方法中,并由子类来实现具体的步骤。两种模式都可以将一系列操作封装起来,但命令模式更注重于解耦请求发送者和接收者,而模版方法模式更注重于定义算法的流程。
- 钩子方法模式(Hook Method Pattern):钩子方法是在模版方法中定义的具体步骤,但其具体实现是空的或默认的。子类可以选择性地重写钩子方法,以便对算法进行扩展或自定义。钩子方法模式可以与模版方法模式一起使用,以提供更灵活的算法实现。
这些设计模式之间有时候会有一些相似之处,但它们各自有不同的用途和关注点。模版方法模式主要用于定义算法的流程,以便在子类中进行具体实现,而其他设计模式则解决不同的问题和场景。因此,选择适当的设计模式取决于具体的需求和设计目标。
5. 模版方法模式的适用场景
- 当你有一个算法的框架,但其中的某些步骤的实现可以在不同的子类中变化时,可以使用模版方法模式。这样可以将算法的公共部分放在父类中,而将具体的实现细节留给子类。
- 当你希望通过继承来扩展一个算法,并且确保不会改变算法的整体结构时,模版方法模式非常有用。子类可以通过重写父类中的方法来添加特定的实现细节,而不会破坏算法的结构。
- 当你希望在一个类中定义一个算法的骨架,但允许具体的步骤由子类来实现时,模版方法模式是一个合适的选择。这样可以确保算法的结构一致,但又允许每个子类根据自己的需要进行定制。
- 当你希望在不破坏封装性的情况下,将一些公共的行为移到父类中时,可以使用模版方法模式。这样可以避免代码重复,提高代码的可维护性和复用性。
总的来说,模版方法模式适用于需要定义算法的整体流程,并允许具体步骤在子类中灵活实现的场景。它能够提供一个抽象的模版,将算法的共同部分抽取出来,同时又能够保持算法的一致性和灵活性。
在Spring框架中,模板方法模式得到了广泛应用。Spring框架提供了多个模板类,这些模板类封装了一系列操作的骨架,并提供了可定制的回调方法,以供开发者在特定步骤插入自己的业务逻辑。
以下是一些常见的在Spring框架中使用模板方法模式的例子:
- JdbcTemplate:JdbcTemplate是Spring框架提供的用于操作数据库的模板类。它封装了数据库连接的获取、资源的管理、异常的处理等一系列操作。开发者可以通过实现JdbcTemplate的回调接口,如PreparedStatementCreator和RowCallbackHandler,在特定的步骤中插入自己的业务逻辑。
- RestTemplate:RestTemplate是Spring框架提供的用于进行HTTP通信的模板类。它封装了HTTP请求的发送、响应的处理等一系列操作。开发者可以通过实现ResponseExtractor接口,在特定的步骤中处理HTTP响应的结果。
- HibernateTemplate:HibernateTemplate是Spring框架提供的用于操作Hibernate的模板类。它封装了Session的获取、事务的管理、异常的处理等一系列操作。开发者可以通过实现HibernateCallback接口,在特定的步骤中执行自己的业务逻辑。
这些模板类提供了一种标准化的方式来处理特定的操作,并在关键步骤中留有可定制的回调方法,以便开发者插入自己的逻辑。这样可以大大简化开发过程,提高代码的可维护性和可测试性。同时,这也是Spring框架中的一种典型的面向切面编程的应用,通过模板方法模式将横切关注点与核心业务逻辑分离,提高了代码的模块化和复用性。
6. 模版方法模式的优缺点
模版方法模式的优点:
- 提供了一种固定的算法骨架,确保了算法的一致性。将算法的公共部分放在抽象父类中,子类只需实现具体的细节,从而保证了算法的结构和顺序。
- 提高了代码的复用性。将算法的公共部分抽取出来,避免了重复编写相似的代码,减少了代码冗余。
- 可以通过继承来扩展和定制算法的具体实现。子类可以根据自己的需要重写父类的方法,从而在不改变算法结构的情况下进行定制化的实现。
- 便于维护和修改。由于算法的公共部分在父类中集中管理,因此对算法的修改只需在父类中进行,无需修改每个子类。
- 实现了开闭原则。通过抽象父类定义算法的骨架,具体的实现由子类来完成,可以在不修改父类的情况下扩展新的子类。
模版方法模式的缺点:
- 类的数量增加。由于需要为每个具体的算法实现创建一个子类,可能导致类的数量增加,增加了代码的复杂性和维护成本。
- 父类对子类的依赖。父类中的模版方法依赖于子类的具体实现,子类的修改可能会影响到父类的稳定性。
- 破坏了单一职责原则。父类需要同时关注算法的结构和流程,可能导致职责不清晰。
总的来说,模版方法模式提供了一种固定的算法骨架,能够提高代码的复用性和可维护性,同时也允许子类进行定制化的实现。但需要注意类的数量增加和父类对子类的依赖带来的一些缺点。因此,在使用模版方法模式时需要权衡利弊,根据具体的情况选择是否使用。
7. 总结
模版方法模式是一种行为设计模式,它定义了一个算法的骨架,将算法中的具体步骤延迟到子类中实现。通过模版方法模式,我们可以在不改变算法整体结构的情况下,允许子类根据自身需求进行个性化的实现。
8. 参考文献
- 韩顺平-Java设计模式