1. 概述
模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
我们说一种设计模式简单,一种是说他的使用场景明确,不会轻易和别的模式弄混,比如单例模式;然而还有一种就是实现简单,模板模式正是后者,它实在不能再简单了,它其实就是抽象类的使用方法。
从问题引入:假如你正在开发一款分析公司文档的数据挖掘程序。 用户需要向程序输入各种格式 (PDF、 DOC 或 CSV) 的文档, 程序则会试图从这些文件中抽取有意义的数据, 并以统一的格式将其返回给用户。
该程序的首个版本仅支持 DOC 文件。 在接下来的一个版本中, 程序能够支持 CSV 文件。 一个月后, 你 “教会” 了程序从 PDF 文件中抽取数据。
一段时间后, 你发现这三个类中包含许多相似代码。 尽管这些类处理不同数据格式的代码完全不同, 但数据处理和分析的代码却几乎完全一样。 如果能在保持算法结构完整的情况下去除重复代码, 这难道不是一件很棒的事情吗?
还有另一个与使用这些类的客户端代码相关的问题: 客户端代码中包含许多条件语句, 以根据不同的处理对象类型选择合适的处理过程。 如果所有处理数据的类都拥有相同的接口或基类, 那么你就可以去除客户端代码中的条件语句, 转而使用多态机制来在处理对象上调用函数。
那怎么做呢?
- 低情商:用抽象类就可以了
- 高情商:用模板方法模式
具体一点来说,其实就是定义一个抽象类,我们都知道抽象类里面可有普通方法和抽象方法,这时普通方法就写子类共有的功能,抽象方法就留给子类重写它们特有的功能。上面的这个例子里,重复的数据处理和分析的代码就可以写到普通方法里,而从不同格式抽离出数据的方法就写成抽象方法,具体子类具体实现这样就OK了。
- 总结:模板方法模式其实就是一种抽象类的使用方法!!!
2. 特点
- 优点
- 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
- 你可将重复代码提取到一个超类中。
- 缺点
- 部分客户端可能会受到算法框架的限制。
- 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
- 模板方法中的步骤越多, 其维护工作就可能会越困难。
- 使用场景
-
当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。
-
当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。 但其后果就是, 只要算法发生变化, 你就可能需要修改所有的类。
在将算法转换为模板方法时, 你可将相似的实现步骤提取到超类中以去除重复代码。 子类间各不同的代码可继续保留在子类中。
-
3. 实现
-
UML类图
-
Java代码
- 模板类
/** * @Author: chy * @Description: 模板类 * @Date: Create in 10:41 2021/3/24 */ public abstract class DataMiner { // 定义好的步骤 void dataAnalyze(){ System.out.println("数据分析"); } // 抽象方法,需要子类实现 abstract void getData(); }
- 具体子类
/** * @Author: chy * @Description: 具体子类,从WORD里面获取数据 * @Date: Create in 10:45 2021/3/24 */ public class WORDDataMiner extends DataMiner{ @Override void getData() { System.out.println("从WORD里获取数据"); } }
/** * @Author: chy * @Description: 具体子类,从PDF里面获取数据 * @Date: Create in 10:44 2021/3/24 */ public class PDFDataMiner extends DataMiner{ @Override void getData() { System.out.println("从PDF里获取数据"); } }
- 客户端
/** * @Author: chy * @Description: 客户端 * @Date: Create in 10:40 2021/3/24 */ public class Client { public static void main(String[] args) { PDFDataMiner pdfDataMiner = new PDFDataMiner(); pdfDataMiner.getData(); pdfDataMiner.dataAnalyze(); System.out.println("============================="); WORDDataMiner wordDataMiner = new WORDDataMiner(); wordDataMiner.getData(); wordDataMiner.dataAnalyze(); } }
- 结果
- 模板类