在地鐵上看《設計模式之禪》之模板方法模式。啊,還有這種模式,待閱讀完全章后,這不就是經常使用的方法。那什么是模板方法模式呢?
一、何為模板方法模式
定義一個操作中的算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
模板方法在抽象類中定義好基本的流程,各個子類根據其具體業務場景實現各個不同的操作。譬如說,我們在日常辦公中經常用的審批流程。審批的流程整體框架是固定的,申請人發起申請,審批人進行簽字審批。但是對于不同的審批(考勤審批,離職審批,貸款審批等等)針對于審批人簽字前和簽字后的處理是不同的。
離職審批:審批完成之后需要解除人員的相關系統權限;請假審批,公司很人性,只需要提個申請單,審批完無其他處理;貸款審批:審批之前需要判斷員工是否符合貸款資質。
二、模板方法通用類圖
三、基于模板方法模式設計的審批程類圖
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
不需修改,這該如何設計呢?
欲解決此問題,請看下一篇《設計模式之工廠方法模式》。