[Design Pattern]組合模式(Composite pattern)

[Design Pattern]組合模式(Composite pattern)

我自己學習 Design Pattern 了解2件很重要的規則,

1.多組合少繼承

但也不是說繼承不好,而是要謹慎使用繼承。

2.多擴充少修改

因新需求擴充新功能,而少修改已經通過測試的程式碼。

 

而這些最終目的就是要讓程式達到高內聚低耦合,anyway~~~

回到真實世界來看一下,大家一定都有寫過依照所屬個別部門單位或部門群組(處),

然後處理相關商業需求邏輯,好比報表顯示、讀取資料...等,

大多Client端可能要先依輸入資料取得相對應部門物件,

這裡面可能也有部門群組的判斷處理(如處別又是另一種商業邏輯),

而且部門群組的異動也將會造成Client端程式碼頻繁修改,

導致Client端的耦合性很高、不容易維護...等,

為了將Client程式碼解耦,我們透過組合模式,將物件組合成為簡單層次結構,

讓Client端可以使用一致的方法來處理組合物件就像處理個別物件一樣(將一對多改為一對一),

並且自己可以處理自己複雜的商業邏輯,

下面我們實際來看一下組合模式可以幫Client程式碼帶來什麼樣的改善。

 

 

UML

image

 

需求:依照部門處理相關商業邏輯

 

大多數人可能會這樣寫Client端程式碼


   string[] Departments = new string[4] { "DepA", "DepB", "DepC","DepAll" };                      

            Factory depfactory = new Factory();
            foreach (string depart in Departments)
            {
                IDepProcess mydep = depfactory.GetDep(depart);
                if (mydep is DepartmentA)//單部門
                {
                    //讀資料
                    mydep.Process();
                }
                else if (mydep is DepartmentB)
                {
                    //更新資料
                    mydep.Process();
                }
                else if (mydep is DepartmentC)
                {
                    //刪除資料
                    mydep.Process();
                }
                else if (mydep is DepartmentGroup)//部門群組
                {
                    //處別的處理 
                    //先確認各部門人數..等
                    mydep.Process();
                    List<IDepProcess> mydeps = (mydep as DepartmentGroup).GetDepartments();
                    foreach (IDepProcess dep in mydeps)
                    {
                        //if(dep is DepartmentA)....
                        //Client繼續處理相關判斷
                    }
                }
            }

            Console.ReadLine();

 

 

執行結果

image

 


 public class Factory
    {
        public Factory() { }

        public IDepProcess GetDep(string Department)
        {
            IDepProcess _IDepProcess = null;
            switch (Department)
            {
                case "DepA":
                    _IDepProcess = new DepartmentA(); 
                    break;
                case "DepB":
                    _IDepProcess = new DepartmentB(); 
                    break;
                case "DepC":
                    _IDepProcess = new DepartmentC(); 
                    break;
                case "DepAll":
                    _IDepProcess = new DepartmentGroup();
                    break;
                default:
                    _IDepProcess = new DepartmentA(); 
                    break;            
            }
            return _IDepProcess; 
        }    
    }

 


 public interface IDepProcess
    {
        void Process();       
    }

 


public class DepartmentA : IDepProcess
    {

        #region IDepProcess 成員

        public void Process()
        {
            Console.WriteLine("部門A處理");
        }

        #endregion
    }

    public class DepartmentB : IDepProcess
    {

        #region IDepProcess 成員

        public void Process()
        {
            Console.WriteLine("部門B處理");
        }

        #endregion
    }

    public class DepartmentC : IDepProcess
    {

        #region IDepProcess 成員

        public void Process()
        {
            Console.WriteLine("部門C處理");
        }

        #endregion
    }

    public class DepartmentGroup : IDepProcess
    {
        protected List<IDepProcess> departments=new List<IDepProcess>();
        public List<IDepProcess> GetDepartments() 
        {
          return departments;
        }

        #region IDepProcess 成員

        public void Process()
        {
            Console.WriteLine("組合部門處理");
        }

        #endregion
    }

 

接下來使用組合模式來重構Client程式碼

            IDepProcessmydep=newDepartmentGroup();
            mydep
.Add(newDepartmentA());

            mydep.Add(newDepartmentB());
            mydep
.Add(newDepartmentC());
            mydep
.Process();

 

執行結果

image

 

修改共同介面


/// <summary>
    /// 共同介面
    /// </summary>
    public interface IDepProcess
    {
        void Process();
        void Add(IDepProcess DepProcess);
        void Remove(IDepProcess DepProcess);
    }

 

 

為了要處理子物件邏輯,所以增加2各方法(Add、Remove)

 

其他類別修改如下


 public class DepartmentA : IDepProcess
    {

        #region IDepProcess 成員

        public void Process()
        {
            Console.WriteLine("部門A處理");
        }  

        public void Add(IDepProcess DepProcess)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void Remove(IDepProcess DepProcess)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }

 

Add 和 Remove 對於沒有子部門(最底層)類別來說沒有任何意義,所以拋出自定義訊息。

 

群組類別修改如下


 public class DepartmentGroup : IDepProcess
    {     
        protected List<IDepProcess> departments = new List<IDepProcess>();

        #region IDepProcess 成員

        public void Add(IDepProcess DepProcess)
        {
            departments.Add(DepProcess);
        }

        public void Remove(IDepProcess DepProcess)
        {
            departments.Remove(DepProcess);
        }      

        public void Process()
        {
            Console.WriteLine("組合部門處理");       
            if (departments.Count > 0)
            {
                foreach (IDepProcess dep in departments)
                {
                    dep.Process();//可能是子部門或其他部門
                }
            }
        }

        #endregion       
    }

 

 

 

從應用角度來看,如果你想把一對多的關係,轉換為一對一的關係(一致性的行為),

那麼你可以看到組合模式所帶來的好處。

 

 

參考

Composite pattern