【读书活动感悟分享】极客充电队——《Head First 设计模式》第八章模板方法模式_文章

【读书活动感悟分享】极客充电队——《Head First 设计模式》第八章模板方法模式

田平洋
发表于 2025-11-10 13:14:41

一、引言

以支付业务开发为例,在日常需求不断的迭代过程中,你会发现所有支付服务类中包含许多相似代码。 尽管这些类处理请求第三方支付平台的代码完全不同, 但参数校验、创建订单等的代码却几乎完全一样。 如何在保持业务逻辑完整的情况下去除重复代码?


二、模板方法设计模式介绍

2.1 定义

模板方法设计模式是一种行为设计模式, 它在一个方法中定义一个算法的骨架,而把一些步骤延迟到子类。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

如上图所示,建房子的流程(打地基 → 立框架→装门窗→封屋顶等)是通用的,但细节可以随着房型不同而变化。

  • 上层流程固定:无论建普通房、别墅还是车库,核心顺序都不变:“地基 + 门 + 窗 + 屋顶”。这样可以抽象出一个buildHouse() 模板方法,内部把步骤写死。
  • 下层细节可变:不同房型对“屋顶”这一步的具体做法不同(平顶、斜顶、双坡顶)。抽象类把这些可变点声明成抽象/钩子方法,交给子类覆写。

2.2 类图

抽象类 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为抽象类型, 也可以提供一些默认实现。

具体类 可以重写所有步骤, 但不能重写模板方法自身。


2.3 适用场景

  • 在多个类中存在相似的算法,但具体实现不同的情况下,可以使用模板方法模式将算法的公共部分抽象出来,由子类实现不同的具体实现。
  • 当需要控制子类的扩展时,可以使用模板方法模式。模板方法模式通过定义抽象类来规范子类的方法实现,子类必须遵从抽象类中定义的方法的规范,这样可以控制子类的扩展。
  • 如果需要一次性编写出框架的核心代码,而将具体实现留给子类来实现,可以使用模板方法模式。


2.4 优缺点

优点

  • 提供了一种代码复用的方式,将实现相同或类似的方法提炼到父类中,减少了代码的重复性。
  • 提供了一种易于维护的方式,由于整体算法结构都在父类中,所以在需要修改时只需要在父类中修改即可,不需要修改每个子类。

缺点

  • 在父类中定义了抽象方法,要求子类实现,如果父类在设计时不够完善,或者需要修改时,会对所有的子类造成影响。
  • 降低了程序的灵活性,当父类增加新的抽象方法时,所有子类都必须进行修改,否则编译会出错。

三、设计模式示例代码

3.1 支付服务重构示例

  • 识别支付服务的算法框架,将算法分解为一系列步骤;


  • 在支付基类中定义以上步骤方法,在模板方法中依次调用这些方法。
  • 步骤可以是抽象的,也可以有一些默认的实现; 支付子类继承支付基类,并实现所有抽象方法,如有必要还需重写一些步骤方法。


3.2 咖啡因饮料制作示例

分析

咖啡和茶的冲泡流程相似(煮水→冲泡→倒杯→加调料),仅 “冲泡” 和 “加调料” 步骤不同。


结构

抽象类(CaffeineBeverage):定义模板方法(prepareRecipe()),包含算法的固定步骤(boilWater()、pourInCup())和抽象步骤(brew()、addCondiments())。


具体子类(Coffee、Tea):实现抽象步骤(Coffee的brew()是 “冲泡咖啡粉”,Tea的brew()是 “浸泡茶叶”)。


3.3 .Net 类库中的示例

System.Data.Common.DbCommand类(数据库操作体系) DbCommand是.NET 中所有数据库命令对象的抽象基类(如SqlCommand、OracleCommand),其定义了数据库命令执行的模板流程ExecuteReader()(执行查询并返回数据阅读器),而具体的数据库交互逻辑(如发送 SQL 到数据库、接收返回结果)则通过抽象方法ExecuteDbDataReader(CommandBehavior behavior)延迟到子类实现


public abstract class DbCommand : IDisposable
{
    public DbDataReader ExecuteReader()
    {
        // 模板方法定义执行逻辑框架
        return ExecuteDbDataReader(CommandBehavior.Default);
    }
    protected abstract DbDataReader ExecuteDbDataReader(CommandBehavior behavior);
}

四、设计模式实践

消息通知服务使用模板方法设计模式实现

需求
  • 需要支持发短信通知、邮件通知、微信公众号通知。
  • 核心流程包括校验参数(VerifyParam)、获取平台账号信息(GetPlatformAccount)、构造消息内容(BuildMsgContent)、发送消息(SendMsg)


UML类图



// 抽象基类
public abstract class BaseNotificationService
{
// 模板方法
public string SendNotification(NotificationParam param)
{
VerifyParam(param);
var account = GetPlatformAccount();
var content = BuildMsgContent(param);
return SendMsg(account, content);
}

// 校验参数(默认实现,子类可重写)
protected virtual void VerifyParam(NotificationParam param)
{
// 通用参数校验逻辑
}

// 获取平台账号信息(默认实现,子类可重写)
protected virtual PlatformAccount GetPlatformAccount()
{
// 通用账号获取逻辑
}

// 构造消息内容(默认实现,子类可重写)
protected virtual string BuildMsgContent(NotificationParam param)
{
// 通用消息构造逻辑
}

// 发送消息(抽象方法,子类必须实现)
protected abstract string SendMsg(PlatformAccount account, string content);
}

// 短信通知子类
public class SmsNotificationService : BaseNotificationService
{
protected override string SendMsg(PlatformAccount account, string content)
{
// 调用短信平台接口发送消息
return "短信发送成功";
}
}

// 邮件通知子类
public class EmailNotificationService : BaseNotificationService
{
protected override string SendMsg(PlatformAccount account, string content)
{
// 调用邮件平台接口发送消息
return "邮件发送成功";
}
}

// 微信公众号通知子类
public class WeChatNotificationService : BaseNotificationService
{
protected override string SendMsg(PlatformAccount account, string content)
{
// 调用微信公众号接口发送消息
return "微信公众号消息发送成功";
}
}

// 辅助类
public class NotificationParam
{
// 通知相关参数(如接收人、消息主题、消息内容等)
}
public class PlatformAccount
{
// 平台账号信息(如账号、密码、接口地址等)
}
117 0

评论


意见反馈