参考文档:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure20
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
应用场景
最近在做第三方厂商的SDK对接,每个厂商都有自己的鉴权体系,基本上都是通过appKey、appSecret去换取Token。由于获取Token的方法可能会被限频,那我们就需要考虑把Token缓存起来;其次,一个Token有效期大约为2个小时,那我们还需要考虑达到某个时间阈值,如过期时间小于30分钟后,我们需要刷新Token。
具体流程图如下:
问题来了,今天我要对接钉钉,我写了一个获取Token的方法并引入了缓存、考虑了Token的刷新,明天我要去对接飞书、北森的开放接口,我发现,这代码简直80%都一样啊!
这里模板方法看起来好像是一个不错的设计模式,我们好像可以引入。
-
模板模式作用一:复用
通过泛型+模板方法流程中不变的东西实现,将GenerateRedisKey、GetToken,让具体的子类实现。
-
模板模式作用二:扩展
现在我需要对接飞书,实现Token的方法,我只需要继承实现TokenTemplate中的GenerateRedisKey、GetToken的方法就可以实现Token的缓存、刷新功能,在不改变原有代码的基础上,添加了新的特性。
BearerToken Token的基类模型
首先我们创建一个BearerToken的abstract基类模型,由于每个厂商接口返回的模型、业务编码可能都不一样的,子类的结果模型需要实现以下两个方法。
/// <summary>
/// 判断Token是否获取成功
/// </summary>
/// <returns></returns>
public abstract bool IsSuccess();
/// <summary>
/// 获取过期时间
/// 单位:s
/// </summary>
/// <returns></returns>
public abstract long GetExpireTime();
复制代码
抽象父类TokenTemplate
DingTalkToken 实现
FeiShuToken 实现
总结
模板方法对于具有流程性的业务逻辑易于扩展和实现,但是如果遇到某个客户端存在独立的特性,比如钉钉和飞书的接口返回值都提供了一个code的业务编码,但是北森接口需要你根据HttpStatusCode(Http状态码)来判断,那我们就需要在模板中添加一个状态码的判断,但实际上钉钉和飞书,并没有Http状态码,这就可能违反里氏替换原则。
软件开发过程成中往往是没有对错的,需要我们多方面的考量和权衡。