模板方法设计模式

2021-01-27 设计模式架构设计

在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

# 应用刨析

假如你正在开发一款分析公司文档的数据挖掘程序。 用户需要向程序输入各种格式 (PDF、 DOC 或 CSV) 的文档, 程序则会试图从这些文件中抽取有意义的数据, 并以统一的格式将其返回给用户。该程序的首个版本仅支持 DOC 文件。 在接下来的一个版本中, 程序能够支持 CSV 文件。 一个月后, 你 “教会” 了程序从 PDF 文件中抽取数据。

数据挖掘类包含许多重复代码

数据挖掘类中包含许多重复代码,一段时间后, 你发现这三个类中包含许多相似代码。 尽管这些类处理不同数据格式的代码完全不同, 但数据处理和分析的代码却几乎完全一样。 如果能在保持算法结构完整的情况下去除重复代码, 这难道不是一件很棒的事情吗?

还有另一个与使用这些类的客户端代码相关的问题: 客户端代码中包含许多条件语句, 以根据不同的处理对象类型选择合适的处理过程。 如果所有处理数据的类都拥有相同的接口或基类, 那么你就可以去除客户端代码中的条件语句, 转而使用多态机制来在处理对象上调用函数。

# 应用场景

  • javaweb里面的Servlet,HttpService类提供了一个service()方法,
  • 有多个子类共有逻辑相同的方法,可以考虑作为模板方法
  • 设计一个系统时知道了算法所需的关键步骤,且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,可以延迟到子类进行完成

代码理解:

public abstract class AbstractClass {

    /** 模版方法 */
    public void templateMethod() {
        specificMethod();
        abstractMethod1();
        abstractMethod2();
    }

    /** 具体方法 */
    public void specificMethod() {
        System.out.println("抽象类中的具体方法被调用");
    }

    // 抽象方法1
    public abstract void abstractMethod1();   

    // 抽象方法2
    public abstract void abstractMethod2(); 
}

# 需求分析

  1. 小明成功晋升为管理者,但是团队来了很多新兵,由于团队水平参差不齐,经常有新项目进来,但整体流程很不规范。
  2. 一个项目的生命周期:需求评审-设计-开发-测试-上线-运维。整个周期里面,需求评审-设计是固定的操作,而其他步骤则流程耗时等是根据项目来定的。
  3. 因此老王梳理了一个模板,来规范化项目,他只管核心步骤和项目里程碑产出的结果,具体的工时安排和开发就让团队成员去操作。

# 代码实现

定义模板,注意processProject方法使用final修饰

public abstract class AbstractProjectManager {

    /**
     * 核心
     * 定义模板方法,定义为final类型,防止子类更改顺序
     */
    public final void processProject(){
        review();
        degisn();
        coding();
        test();
        online();
    }

    /**
     * 每个项目都需要评审
     */
    public void review(){
        System.out.println("项目需求评审");
    }

    /**
     * 每个项目都需要UI设计
     */
    public void degisn(){
        System.out.println("UI 进行设计");
    }

    /**
     * 抽象方法,由具体子类进行实现,编码耗时不一样
     */
    public abstract void coding();

    /**
     * 抽象方法,由具体子类进行实现,自动化测试、压力测试等等
     */
    public abstract void test();


    /**
     * 抽象方法,由具体子类进行实现,上线
     */
    public abstract void online();

}

# 支付功能和用户功能的交付

public class PayServerProjectManager extends AbstractProjectManager{
    @Override
    public void coding() {
        System.out.println("开发耗时30天");
    }

    @Override
    public void test() {
        System.out.println("功能测试,压力测试");
    }

    @Override
    public void online() {
        System.out.println("全量上线");
    }
}
public class UserServerProjectManager extends AbstractProjectManager{
    @Override
    public void coding() {
        System.out.println("开发耗时5天");
    }

    @Override
    public void test() {
        System.out.println("功能测试,手工测试,压力测试");
    }

    @Override
    public void online() {
        System.out.println("灰度发布,全量上线");
    }
}

# 测试

public static void main(String[] args) {
    AbstractProjectManager projectManager;
    projectManager = new PayServerProjectManager();
    projectManager.processProject();
    System.out.println("=========");
    projectManager = new UserServerProjectManager();
    projectManager.processProject();
}

控制台(结果输出保持顺序性)

项目需求评审
UI 进行设计
开发耗时30天
功能测试,压力测试
全量上线
=========
项目需求评审
UI 进行设计
开发耗时5天
功能测试,手工测试,压力测试
灰度发布,全量上线

# 小结

优点:

  • 扩展性好,对不变的代码进行封装,对可变的进行扩展,符合开闭原则

  • 提高代码复用性 将相同部分的代码放在抽象的父类中,将不同的代码放入不同的子类中

    • 通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制

缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,会使系统变得复杂

上次更新: 1 年前