C#中的TemplateMethod模式

一個真實的故事

大學的時候就開過一門課程,講設計模式,但是大學生沒什麼編程實踐經驗,在大學裏面聽設計模式的感受,就像聽天書。聽着都有道理,但是徹底領會不到其中的奧妙,大抵緣由就在於沒有走過彎路,沒有吃過設計不當的虧。古人云,「操千曲然後曉聲,觀千劍然後識器」,誠不欺我。
 
博主在以前的某個項目中,設計出了一些工具類,像屬性窗口,錯誤提示窗口,還有一個窗口管理類管理它們,當時我實現工具保存時候的代碼是這樣的:

編程

class WindowManager
    {
        private List<ITool> _Tools = new List<ITool>();        

        public void AddTool(ITool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    interface ITool
    {
        bool BeforeSave();
        void Save();
        void AfterSave();
    }

    class PropertyWindow : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

    class ErrorLis : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

當時博主對這段代碼還挺滿意,徹底沒有看出這兒有什麼問題,以爲這簡直寫的太OO了,有類,有接口,有針對接口編程,至於新加的工具類,也不會影響原來的代碼,簡直太符合開閉原則了。老鐵,沒毛病!
 
好日子就這麼繼續下去,每當須要新添加一個工具,我就新加一個類,在類裏面實現Save的邏輯,直到有一天,添加了一個ResourceControl

設計模式

class ResourceControl : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (!BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

 
在它的save裏面,我把if(BeforeSave())寫成了if(!BeforeSave())。。。
因而,我又額外花了一些時間來找到這個問題,修改它並在下次添加新類的時候戰戰兢兢提醒本身不要犯這種低級的錯誤。那麼,咱們有沒有好的辦法來解決這個問題呢?

ide

問題分析

其實就算每次添加新類的時候咱們都能仔細的當心避免維護相同的邏輯,這段代碼的設計也仍是有能夠改進的地方,好比,BeforeSave和AfterSave在這裏做爲接口ITool的一部分而公開,意味着客戶代碼能夠自由的調用BeforeSave和AfterSave,然而這極可能並非代碼做者的本意,畢竟,不調用Save而單獨調用BeforeSave和AfterSave有什麼意義呢?讓客戶可以看到更多沒必要要的方法,增長了客戶錯誤使用接口的可能性,不是麼?
 
綜上所述,咱們須要解決的問題以下:

工具

  • 抽象出Save, BeforeSave和AfterSave的邏輯關係,在一個地方固定下來,確保新增長的類所實現的這三個方法,都能自動具備這種邏輯關係。
  • 對客戶代碼隱藏沒必要要的接口。
     
    這種場景下面,咱們須要用到設計模式中的TemplateMethod(模版方法)模式。
     


TemplateMethod模式

WIKI上面,TemplateMethod模式的定義以下,
In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm's structure.
url

大概意思就是,模版方法模式是一種行爲類設計模式,容許軟件在更高的層次定義程序骨架,可是能夠在子類推遲實現某些步驟。
 
類圖以下:



spa

這徹底符合咱們的需求,讓咱們試着修改咱們的代碼。
 
.net

使用TemplateMethod從新實現的代碼

class WindowManager
    {
        private List<AbstractTool> _Tools = new List<AbstractTool>();        

        public void AddTool(AbstractTool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    abstract class AbstractTool
    {
        protected abstract bool BeforeSave();
        protected abstract void DoSave();
        protected abstract void AfterSave();
        public void Save()
        {
            if(!BeforeSave())
            {
                DoSave();
                AfterSave();
            }

        }        
    }

    class PropertyWindow : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {
            
        }

        protected override void AfterSave()
        {

        }
    }

    class ErrorLis : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {

        }

        protected override void AfterSave()
        {

        }
    }

從上面咱們能夠看到,咱們用一個抽象類AbstractTool代替以前的ITool接口,抽象類和接口的一個區別就是,抽象類能夠在其中嵌入某些邏輯,因此咱們在Save這個公共的非虛方法中,徹底實現了咱們的BeforeSave和AfterSave邏輯,僅僅留下了BeforeSave,AfterSave和DoSave給子類覆蓋。這樣咱們獲得的好處是:設計

  • 抽象類只公開了一個Save方法,因此客戶代碼不用擔憂會調用其餘錯誤的方法。
  • 抽象類徹底固定了Save邏輯,先調用BeforeSave檢查,以後執行DoSave進行具體的Save事項,最後進行AfterSave行爲。子類只須要從新依據子類的需求覆蓋這三個虛方法便可。新添加的工具類,只要覆蓋這三個虛方法,至於虛方法之間的邏輯,抽象類已經固定,不用擔憂。

結論

「紙上得來終覺淺,絕知此事要躬行」,祖宗的話,不會錯的,若是沒有必定的編程實踐和總結,是沒有辦法領悟設計模式的,博主也是經過以前那個例子才領悟到TemplateMethod模式的妙用。但願你們多多編程,早日領悟。3d

相關文章
相關標籤/搜索