剝殼模式 ( Decorticating Pattern )

這是我最近在 MSDN 論壇看到的一個有趣問題, 解決之後就很無聊地給了它一個名字 -- 剝殼模式, 所以你如果翻遍設計模式的書也是找不到的(這命名實際上有點搞笑的意味).

        這是我最近在 MSDN 論壇看到的一個有趣問題, 解決之後就很無聊地給了它一個名字 -- 剝殼模式, 所以你如果翻遍設計模式的書也是找不到的(這命名實際上有點搞笑的意味).

 

 

        先要講講這問題的背景 (參考 ㄧ種字串組合語法 ) , 大意是這位發問者想要得到一種效果是在編輯時期就可以限制住其方法鏈上的順序和選項數目. 根據發問者的描述, 這個需求有幾個限制:

        (1) 在編輯時期使用方法鏈串接不同的命令

        (2) 命令的順序限制為以下各種形式 (L1 代表第一層命令, L2 代表第二層命令, L3 代表第三層命令, Result 代表取得最終結果的命令)

            L1 => Result

            L1 => L2 => Result
            L1 => L2  => L3 => Result

            L1 => L3 => Result

            L2 => Result

            L2  => L3 => Result

            L3 => Result

 

 

        從上面的限制分析, 若要以繼承的方式完成, 其基底類別會是 Result (取得結果) 的那個工作, L3 繼承 Reuslt, L2 繼承 L3, L1 繼承 L2, 而每呼叫一次方法則將該方法的回傳值型別設定成該方法所在型別的父型別 (這就是為什麼我會稱它為『剝殼』, 因為它在使用的時候就是一層層剝掉它的型別) . 聽起來有點模糊, 直接看程式碼.

 

 


   public abstract class DecorticatingResult
    {
        public string Result { get; protected set;}

        protected DecorticatingResult()
        {
            Result = string.Empty;
        }
    }

    public abstract class DecorticatingCommandL3 : DecorticatingResult
    {
        public DecorticatingResult AAA()
        {
            Result += "探病";
            return this;
        }

        public DecorticatingResult BBB()
        {
            Result += "讀書";
            return this;
        }

        public DecorticatingResult CCC()
        {
            Result += "購物";
            return this;
        }
    }

    public abstract class DecorticatingCommandL2 : DecorticatingCommandL3
    {
        public DecorticatingCommandL3 AA()
        {
            Result += "去醫院";
            return this;
        }

        public DecorticatingCommandL3 BB()
        {
            Result += "去學校";
            return this;
        }

        public DecorticatingCommandL3 CC()
        {
            Result += "去商店";
            return this;
        }
    }

    public class DecorticatingCommandL1 : DecorticatingCommandL2
    {
        public DecorticatingCommandL2 A()
        {
            Result += "我";
            return this;
        }

        public DecorticatingCommandL2 B()
        {
            Result += "你";
            return this;
        }

        public DecorticatingCommandL2 C()
        {
            Result += "他";
            return this;
        }
    } 

 

 

        以下為使用這些類別的操作畫面, 有達到預期的效果.

2015-05-26_17-30-46

2015-05-26_17-31-20

2015-05-26_17-31-48

 

 

        看來似乎問題是解決了, 可是我真的很不喜歡這樣的方式. 這種完全使用繼承去達成的作法有幾個還算滿嚴重的問題. 第一個是它根本不具擴展性, 完全違反開閉原則, 如果要在中間再插入一層命令, 就會是一個災難的開始. 第二個是在客戶端的呼叫會變成以實作類別為主, 而非以抽象為主, 這又違反了倚賴倒置原則. 再者則是過度使用繼承會使得每個類別之間過度倚賴. 不過, 這個方式也有個優點, 就是簡單易懂, 若是命令層級根本不會有變動的時候, 這是一種解決方法, 雖然並不美麗, 但很有效. 我在思考的過程還想出另外一個方式 -- 利用明確介面實作來完成類似的效果, 但那個方法更糟, 會有許多冗餘程式碼出現, 而且除了搞死自己, 大概沒甚麼優點, 就不必再介紹它了.

 

 

        下一篇將會來聊聊如何使用不同的思考方向, 改變這個剝殼式設計, 讓它能夠稍微改善一些缺點.