在地铁上看《设计模式之禅》之模板方法模式。啊,还有这种模式,待阅读完全章后,这不就是经常使用的方法。那什么是模板方法模式呢?
一、何为模板方法模式
定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法在抽象类中定义好基本的流程,各个子类根据其具体业务场景实现各个不同的操作。譬如说,我们在日常办公中经常用的审批流程。审批的流程整体框架是固定的,申请人发起申请,审批人进行签字审批。但是对于不同的审批(考勤审批,离职审批,贷款审批等等)针对于审批人签字前和签字后的处理是不同的。
离职审批:审批完成之后需要解除人员的相关系统权限;请假审批,公司很人性,只需要提个申请单,审批完无其他处理;贷款审批:审批之前需要判断员工是否符合贷款资质。
二、模板方法通用类图
三、基于模板方法模式设计的审批程类图
ApprovalProcessBase 中Sign
方法为模板方法,处理整体审批流程,SignBefore
和SignAfter
在考勤审批,离职审批,贷款审批有不同的处理。
介绍了这么多,我们来看看具体的代码示例。
四、代码示例
先来看看抽象类ApprovalProcessBase
的实现
public abstract class ApprovalProcessBase
{
public virtual bool SignBefore(Approval model)
{
return true;
}
public virtual bool SignAfter(Approval model)
{
return true;
}
public bool Sign(Approval model)
{
var signBefore = this.SignBefore(model);
var sign = SignHandle(model);
//进行签字后操作
return this.SignAfter(model);
}
/// <summary>
/// 进行签字
/// </summary>
/// <param name=""></param>
/// <returns></returns>
private bool SignHandle(Approval model)
{
//校验签字人密码等等
return true;
}
}
各个实现类的实现。
公司管理很人性,请假只需要提交申请单,审批前和审批后无需任何处理。默认使用抽象类中ApprovalProcessBase
定义的处理方式
public class AttendanceProcess: ApprovalProcessBase
{
}
离职审批则不同了,审批后需要解除员工权限
public class LeaveProcess: ApprovalProcessBase
{
public override bool SignAfter(Approval model)
{
//解除员工权限
return RemoveAuth();
}
}
假若公司规定,在公司工作超过3年,才可以向公司低息贷款,那么贷款申请审批,需要在审批前,验证员工的贷款资质
public class LoanProcess: ApprovalProcessBase
{
public override bool SignBefotr(Approval model)
{
//验证员工贷款资质
return ValidifEmployeeCertificate();
}
}
五、模板方法模式的扩展
在通常情况下,签字前签字后处理的结果影响审批流程的处理。贷款审批,签字前进行资质验证,若员工不满足贷款条件,则不允许贷款,不能通过签字,审批不通过。也就是抽象类ApprovalProcessBase
中方法Sign
依赖于SignBefore
SignAfter
的执行结果,那该如何设计呢?可以使用钩子方法(Hook Method)。
钩子方法:抽象类中定义的一个空实现的方法,子类可以实现它,从而改变父类模板方法的执行逻辑。在
C#
定义为虚方法,关键词virtual
,Java
中为abstract
。
看到这里,你也许会说那SignBefore
和SIgnAfter
不就是钩子方法吗?是的,他们就是钩子方法,通过各自的条件改变,影响模板方法的执行。我们就可以将Sign
方法做以下修改:
public bool Sign(Approval model)
{
var signBefore = this.SignBefore(model);
if (!signBefore)
{
SendEmpolyeeNotice();//通知员工失败原因
return false;
}
var sign = SignHandle(model);
//进行签字后操作
var signAfter= this.SignAfter(model);
if (signAfter)
{
SendEmployeeNoteceOfSuccess();//通知用户成功
return true;
}
CallBackTheOperation();//失败将前面操作回滚
return false;
}
调用类
public class HandleApprove
{
public bool Approve(string type)
{
switch (type)
{
case "Attendance":
return new AttendanceProcess().Sign();
case "Leave":
return new LeaveProcess().Sign();
case "Loan":
return new LoanProcess().Sign();
default:
return false;
}
}
}
六、模板方法模式优点
通过这个例子我们可以看到模板方法有以下几个优点:
- 封装,可扩展:将不变的地方封装到父类实现,将可变的部分通过继承在子类继续扩展;
- 提取公共代码,便于维护。
七、模板方法模式适用场景
- 代码重构时:将相同的代码抽取到父类,然后通过钩子函数,约束其行为;
- 多个子类有公有的方法,且逻辑基本相同;
- 重要,复杂的算法,把核心算法设计为模板方法,其他相关细节功能由各个子类实现。
审批流程设计好了。现在有离职审批LeaveProcess
,考勤审批AttendanceProcess
,贷款审批LoanProcess
,日后会增加其他的如报销审批,转正审批等等,那么每增加一个审批类别,就需要修改HandleApprove
,扩展性不佳。希望审批种类增加,但是审批处理类HandleApprove
不需修改,这该如何设计呢?
欲解决此问题,请看下一篇《设计模式之工厂方法模式》。