前言:
模板方法(Template Method)属于设计模式中的行为模式,它的特点是:定义一个操作中的算法的 框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可 重定义该算法的某些特定步骤。
优点:
- 封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 父类中提取了公共的部分代码,便于代码复用。
- 部分方法(通常是指不共用的方法)是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
场景与实现:
初中校园作息时间和高中校园作息时间都差不多,举例几个共同点:上早自习,课间操,午休,下午课,放学。只是时间上可能存在一些区别。因此可以将这些行为抽象并抽取相同的行为定义为一个模板。然后继承这套模板,分别实现各自的作息时间。
分析实现:
1.定义一个抽象类:AbstractSchoolLifeTemplate 将共同的行为定义成抽象方法,等待子类实现
2.创建初中生活作息类MiddleSchoolLife、高中生活作息类 SeniorSchoolLife,都分别继承AbstractSchoolLifeTemplate 并实现抽象方法。
3.创建测试类测试
代码实现:
项目结构图:
主要就是一个作息时间抽象类,两个具体实现类,一个测试类
相关类源码:
作息时间抽象类:AbstractSchoolLifeTemplate
这个类定义了几个抽象方法,等待子类实现,还写了一个公共方法(请看上面优点2:父类中提取了公共的部分代码,也就是不变代码,便于代码复用)来调用各个抽象方法,用来实现一天的作息时间表的输出
package com.dp.template;
/**
* 校园生活作息抽象类
*/
public abstract class AbstractSchoolLifeTemplate {
public String studentType;
//早读课
public abstract void moring();
//课间操
public abstract void morningExercises();
//午休
public abstract void siesta();
//下午课
public abstract void afternoonClasses();
//放学
public abstract void afterSchool();
//学校的一天
public void SchoolLifeOfDay(){
System.out.println(studentType+"学校作息时间安排:");
this.moring();
this.morningExercises();
this.siesta();
this.afternoonClasses();
this.afterSchool();
}
}
初中作息实现类:MiddleSchoolLife
这个类继承了抽象类AbstractSchoolLifeTemplate ,必须实现AbstractSchoolLifeTemplate 定义的抽象方法
package com.dp.template;
/**
* 初中作息实现类
*/
public class MiddleSchoolLife extends AbstractSchoolLifeTemplate{
public MiddleSchoolLife(String studentType){
this.studentType = studentType;
}
@Override
public void moring() {
System.out.println("8:00 上早自习!");
}
@Override
public void morningExercises() {
System.out.println("9:30 课间操");
}
@Override
public void siesta() {
System.out.println("12:00-14:00 午休");
}
@Override
public void afternoonClasses() {
System.out.println("14:00 上课");
}
@Override
public void afterSchool() {
System.out.println("16:30 放学");
}
}
高中作息实现类:SeniorSchoolLife
这个类和初中作息时间类一样的目的,实现高中作息具体时间
package com.dp.template;
/**
* 高中作息实现类
*/
public class SeniorSchoolLife extends AbstractSchoolLifeTemplate{
public SeniorSchoolLife(String studentType){
this.studentType = studentType;
}
@Override
public void moring() {
System.out.println("7.30 上早自习!");
}
@Override
public void morningExercises() {
System.out.println("10:00 课间操");
}
@Override
public void siesta() {
System.out.println("12:00-14:30 午休");
}
@Override
public void afternoonClasses() {
System.out.println("14:30 上课");
}
@Override
public void afterSchool() {
System.out.println("17:10 放学");
}
}
测试类:TestMain
package com.dp.template;
public class TestMain {
public static void main(String[] args) {
//创建初中作息类
AbstractSchoolLifeTemplate middleSchoolLife = new MiddleSchoolLife("初中生");
//创建高中作息类
AbstractSchoolLifeTemplate seniorSchoolLife = new SeniorSchoolLife("高中生");
//调用父类的作息安排方法(共同)
middleSchoolLife.SchoolLifeOfDay();
System.out.println("========================");
//调用父类的作息安排方法(共同)
seniorSchoolLife.SchoolLifeOfDay();
}
}
测试结果:
总结:
模板方法是一个比较实用的模式,有看过 Dubbo源码的朋友应该知道,里面大量代码运用了模板方法设计模式,为什么 Dubbo 可以支持很多种注册中心?其实本质就是用了模板方法设计模式,使得可以扩展多种注册中心。掌握好模板方法,对读源码有非常大的帮助。不懂设计模式去阅读源码的时候,可能会感觉那些代码那么绕?调来调去的。 当你了解了常用的设计模式之后,看源代码就可以直截了当的知道是用什么设计模式,为什么用这个设计模式?目的是什么?有了这层思考, 就像有一条线将以前散落在各地的知识点连接起来,成了可以推敲的知识。