Design Pattern - Composite

(史帝芬, 2007/06/03, hi.steven@gmail.com)
    在看Composite Pattern之前,建議先看一下Iterator Pattern, Iterator Pattern可巡訪聚合物件裡的元素,而這些元素是同類別的。Composite Pattern也會有巡訪的動作, 巡訪的類別會有個別類別物件和組合類別物件,但是Composite Pattern會將物件組織成樹狀結構, 並且讓外界以一致性的方式對待個別類別物件和組合類別物件。下圖是GoF書中Composite Pattern的類別圖, 個別類別Left和組合類別Composite都繼承Component,透過這樣的繼承關係,才能使得巡訪時, 可將個別類別物件及組合類別物件視為相同的類別。

    這裡我們將以檔案系統為例,實作Composite Pattern,類別圖如下。 個別類別File及組合類別Directory都繼承Entry,Directory可包含多個File及多個Directory, 實際巡訪元素時,操作的是Entry,所以,也就是將這兩個不同類別的物件視為相同,實際上的 實作是在File和Directory中, 而非在Entry。

  • 程式 (C#)

  • using System;
    using System.Collections;
    
    namespace Composite
    {
        public abstract class Entry
        {
            private string name;
    
            public Entry(string name)
            {
                this.name = name;
            }
    
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
    
            public virtual int Size
            {
                get { return 0; }
            }
    
            public virtual Entry add(Entry entry)
            {
                throw new Exception();
            }
    
            public abstract void printList(string prefix);
    
            public void printList()
            {
                printList("");
            }
        }
    
        public class File : Entry
        {
            private int size;
    
            public File(string name, int size)
                : base(name)
            {
                this.size = size;
            }
    
            public override int Size
            {
                get { return size; }
            }
    
            public override void printList(string prefix)
            {
                Console.WriteLine("{0}/{1} ({2})", prefix, Name, Size);
            }
        }
    
        public class Directory : Entry
        {
            private ArrayList directory = new ArrayList();
    
            public Directory(string name)
                : base(name)
            {
    
            }
    
            public override int Size
            {
                get
                {
                    int size = 0;
                    foreach (Entry entry in directory)
                    {
                        size += entry.Size;
                    }
    
                    return size;
                }
            }
    
            public override Entry add(Entry entry)
            {
                directory.Add(entry);
                return this;
            }
    
            public override void printList(string prefix)
            {
                Console.WriteLine("{0}/{1} ({2})", prefix, Name, Size);
                foreach (Entry entry in directory)
                {
                    entry.printList(prefix + "/" + Name);
                }
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Directory windows = new Directory("Windows");
                Directory system32 = new Directory("system32");
                windows.add(system32);
                windows.add(new File("desktop.ini", 923));
                windows.add(new File("TASKMAN.EXE", 15360));
                system32.add(new File("shutdown.exe", 23552));
    
                windows.printList();
    
                Console.ReadLine();
            }
        }
    }
    

  • 執行結果

  • /Windows (39835)
    /Windows/system32 (23552)
    /Windows/system32/shutdown.exe (23552)
    /Windows/desktop.ini (923)
    /Windows/TASKMAN.EXE (15360)