模版模式

"""
模板方法模式作为一种行为设计模式,在超类中构建了一个算法框架,使得子类能够在不改变整体结构的前提下,对算法的特定步骤进行重写。

假设正在开发一款用于分析公司文档的数据挖掘程序。用户需向该程序输入多种格式(如PDF、DOC或CSV)的文档,程序则负责从这些文件中提取有意义的数据,并以统一格式返回给用户。此程序的初始版本仅支持DOC文件。在后续的版本中,程序增添了对CSV文件的支持。一个月后,程序又具备了从PDF文件中提取数据的能力。image

经过一段时间的开发,发现这三个类中存在大量相似代码。尽管这些类处理不同数据格式的代码差异显著,但数据处理与分析的代码却近乎一致。若能在维持算法结构完整的情况下,去除重复代码,无疑是极为理想的。

此外,还存在另一个与使用这些类的客户端代码相关的问题:客户端代码中包含诸多条件语句,用于依据不同的处理对象类型,选择合适的处理流程。倘若所有处理数据的类都拥有相同的接口或基类,那么便可以去除客户端代码中的条件语句,转而运用多态机制,在处理对象上调用函数。

解决方案
模板方法模式主张将算法拆解为一系列步骤,随后将这些步骤转化为方法,最终在“模板方法”中按顺序调用这些方法。这些步骤既可以是抽象的,也可以具备一些默认实现。为了能够运用该算法,客户端需要自行提供子类,并实现所有的抽象步骤。如有必要,还需重写部分步骤(但不包括模板方法本身)。image

模板方法将算法分解为步骤,并允许子类重写这些步骤,而不是重写实际的模板方法。

首先,将所有步骤声明为抽象类型,强制要求子类自行实现这些方法。在本示例中,子类已具备所有必要的实现,因此只需调整这些方法的签名,使其与超类的方法相匹配。

接下来,探讨如何去除重复代码。对于不同的数据格式,文件的打开与关闭以及数据的抽取和解析代码各不相同,所以无需对这些方法进行修改。然而,分析原始数据和生成报告等其他步骤的实现方式极为相似,故而可将其提取至基类中,以供子类共享。

如我们所见,存在两种类型的步骤:

  • 抽象步骤:必须由各个子类来实现。
  • 可选步骤:已有一些默认实现,但在必要时仍可进行重写。

此外,还有一种名为钩子的步骤。钩子是内容为空的可选步骤。即便不重写钩子,模板方法依旧能够正常工作。钩子通常设置在算法重要步骤的前后,为子类提供额外的算法扩展点。

模板方法模式结构

  • 抽象类(Abstract­Class):会声明作为算法步骤的方法,以及依次调用它们的实际模板方法。算法步骤既可以被声明为抽象类型,也可以提供一些默认实现。
  • 具体类(Concrete­Class):可以重写所有步骤,但不能重写模板方法自身。image

考虑在数据挖掘应用中如何实现上述方案。我们可为图中的三个解析算法创建一个基类,该类将定义调用了一系列不同文档处理步骤的模板方法。

模板方法模式适合应用场景

  • 当你仅期望客户端扩展某个特定算法步骤,而非整个算法或其结构时,可采用模板方法模式。模板方法将整个算法转化为一系列独立的步骤,便于子类对其进行扩展,同时还能使超类中所定义的结构保持完整。
  • 当多个类的算法除一些细微差异外几乎完全相同时,可运用该模式。但需要注意的是,一旦算法发生变化,可能需要对所有相关类进行修改。在将算法转换为模板方法时,可将相似的实现步骤提取到超类中,以去除重复代码。子类间存在差异的代码可继续保留在子类中。

实现方式

  • 分析目标算法,判断其是否能够分解为多个步骤。从所有子类的角度出发,明确哪些步骤具有通用性,哪些步骤存在差异。
  • 创建抽象基类,并声明一个模板方法以及代表算法步骤的一系列抽象方法。在模板方法中,依据算法结构按顺序调用相应步骤。可使用final关键字修饰模板方法,以防止子类对其进行重写。
  • 虽然可以将所有步骤都设置为抽象类型,但为部分步骤提供默认实现可能会带来益处,因为子类无需实现这些方法。
  • 可考虑在算法的关键步骤之间添加钩子。
  • 为每个算法变体创建一个具体子类,该子类必须实现所有的抽象步骤,也可以重写部分可选步骤。

模板方法模式优缺点

  • 优点
    • 仅允许客户端重写大型算法中的特定部分,降低算法其他部分修改对其造成的影响。
    • 能够将重复代码提取到一个超类中。
  • 缺点
    • 部分客户端可能会受到算法框架的限制。
    • 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
    • 模板方法中的步骤越多,其维护工作可能就越困难。

与其他模式的关系

  • 工厂方法模式是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型模板方法中的一个步骤。
  • 模板方法基于继承机制:它允许你通过扩展子类中的部分内容来改变部分算法。策略模式基于组合机制:你可以通过对相应行为提供不同的策略来改变对象的部分行为。模板方法在类层次上运作,因此它是静态的。策略在对象层次上运作,因此允许在运行时切换行为。
    """
posted @ 2025-07-07 14:05  kisshappyboy  阅读(8)  评论(0)    收藏  举报