2019年9月26日 星期四

C# Design Pattern - Composite 組合模式


Composite,單字的意思就是「綜合、合成的、合成物……..(想不到了)」,意思是一樣東西,他可以有許多不同的元素,或者功能,或者是特性等等。

所以我們在實現在Composite這個Design Pattern的時候,只要往這固方向去思考,其實可以找到一個方向,是不是這麼說的??是不是是不是是不是。

好,接下來就用你我的想像力,來了喔,要想像,我們想像我們在寫一個軟體時候,想一個方向是:圖像的本質,也就是圖像XDDD。
所以圖像就可以呈現任何的可能,可以是一隻鳥,一個正妹,一個帥氣的我(無誤)。

然後我們就定義一個名稱叫「Graphics」,然後它可以做的事情,就是Draw,像這樣:

public abstract class Graphics
{
    protected string _name;

    public Graphics(string name)
    {
        this._name = name;
    }

    public abstract void Draw();
}

然後我們再製造一樣東西和「Graphics」很相似的東西,但更具體,就叫它「Picture」。「Picture」它可以有和「Graphics」相似的元素,然後可以實現更具體的事情,它一樣可以畫圖。然後一樣複製了相同相以的東西,如「Line」、「Circle」、「Rectangle」。但它面都和「Picture」一樣,可以有相似的特性和元素,接著我們就實現這樣的程式結構:

public class Picture : Graphics
{
    public Picture(string name) : base(name) { }

    public override void Draw() { Console.WriteLine("Draw Picture Role Name" + _name.ToString()); }

    public ArrayList GetChilds()
    {

        //返回所有的子物件
        ArrayList a = new ArrayList();
        return a;

    }
}

public class Line : Graphics
{

    public Line(string name) : base(name) { }

    public override void Draw()
    {

        Console.WriteLine("Draw a" + _name.ToString());

    }

}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }
}



public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }
}

然後我們就可以依序將每個相似特性執行看看。
Picture picture = new Picture("Picture");
picture.Draw();

Line line = new Line("Line");
line.Draw();

Circle circle = new Circle("Circle");
circle.Draw();

Rectangle rectangle = new Rectangle("Rectangle");
rectangle.Draw();


看到下面呈現的結果後,我們可以發現,這四個類別都有非常相似的特性:




但這樣如果分散著寫的話,就會覺得有點笨,畢竟時代在進步,寫程式的Level也要提升。
這時候我們要對結構做一點加工,將一些相同基本圖像元素(直線、圓等)以及一些複合圖像元素(由基本圖像元素組合而成)建構一起。在設計中對每一個物件都配一個Draw()方法,在調用時,會顯示相關的圖形。可以看到這裡複合圖像元素它在充當物件的同時,又是那些基本圖像元素的一個容器。

public abstract class Graphics
{
    protected string _name;
    public Graphics(string name)
    {
        this._name = name;
    }

    public abstract void Draw();
    public abstract void Add(Graphics g);
    public abstract void Remove();

}

要對相似圖像元素進行處理,在程式中,需要判斷返回物件的具體類型到底是基本圖像元素,還是複合圖像元素。如果是複合圖像元素,要用遞迴去處理, 然而這種處理的結果卻增加了用戶端程式與複雜圖像元素內部結構之間的依賴,這會程式在執行時看起來很笨拙,會有什麼樣的情況這邊在此先不討論,之後會延伸話題,不過在此說明,其實程式沒有一定最標準的寫法,任何一種技巧都存在著不同情境,不同效果、不同的商業需求而產生的優勢與劣勢。

如上面的程式,讓程式可以像處理基本圖像元素一樣來處理複合圖像元素,這就要引入Composite模式了,需要把對於子物件的管理工作交給複合圖像元素,為了進行子物件的管理,必須提供必要的Add()Remove()等方法(名稱是可以自行定義的,想定什麼可以天馬行空的去想)

public abstract class Graphics
{
    protected string _name;
    public Graphics(string name)
    {
        this._name = name;
    }

    public abstract void Draw();
    public abstract void Add(Graphics g);
    public abstract void Remove();
}

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();
    public Picture(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Picture" + _name.ToString());
        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

    public override void Add(Graphics g)
    {
        picList.Add(g);
    }
    public override void Remove(Graphics g)
    {
        picList.Remove(g);
    }
}

public class Line : Graphics

{
    public Line(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Line role" + _name.ToString());
    }
    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

要對圖像元素進行處理,在程式裡需要判斷返回物件的具體類型到底是基本圖像元素,還是複合圖像元素,複合,就是多重且有重覆的意思,所以就是多重重覆的圖像元素(解釋完畢)。如果是複合圖像元素,要用遞迴去處理, 但這種處理的結果會增加了在建構程式時產生複雜結構,重覆的語句會變的太多。要讓程式可以像處理基本圖像元素一樣來處理複合圖像元素,這就要引入Composite模式了,需要把對於子物件的管理工作交給複合圖像元素,為了進行子物件的管理,它必須提供必要的Add()Remove()等方法。

看一下下面的例子(要好好看啊):

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();
    public Picture(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Picture" + _name.ToString());

        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

    public override void Add(Graphics g)
    {
        picList.Add(g);
    }

    public override void Remove(Graphics g)
    {
        picList.Remove(g);
    }
}

public class Line : Graphics
{
    public Line(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Line role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

public class Circle : Graphics
{
    public Circle(string name) : base(name) { }
    public override void Draw()
    {
        Console.WriteLine("Draw Circle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}



public class Rectangle : Graphics
{
    public Rectangle(string name) : base(name) { }

    public override void Draw()
    {
        Console.WriteLine("Draw Rectangle role" + _name.ToString());
    }

    public override void Add(Graphics g) { }
    public override void Remove(Graphics g) { }
}

這樣引入Composite模式後,程式不再依賴複合圖像元素的內部方法,執行時程式結構就不用那麼牛笨。我們可以再讓程式更加進化:

public abstract class Graphics
{
    protected string _name;

    public Graphics(string name)
    {
        this._name = name;
    }
    public abstract void Draw();
}

public class Picture : Graphics
{
    protected ArrayList picList = new ArrayList();

    public Picture(string name)
        : base(name)
    { }
    public override void Draw()
    {
        Console.WriteLine("Draw Picture:" + _name.ToString());

        foreach (Graphics g in picList)
        {
            g.Draw();
        }
    }

    public void Add(Graphics g)
    {
        picList.Add(g);
    }
    public void Remove(Graphics g)
    {
        picList.Remove(g);
    }
}

public class Line : Graphics
{
    public Line(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw role" + _name.ToString());
    }
}

public class Circle : Graphics
{
    public Circle(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw role:" + _name.ToString());
    }
}

public class Rectangle : Graphics
{
    public Rectangle(string name)
        : base(name)
    { }

    public override void Draw()
    {
        Console.WriteLine("Draw Role" + _name.ToString());
    }
}

這種方式屬於安全式的Composite模式,通過Composite模式,程式在調用Draw()的時候不用再去判斷複雜圖像元素中的子物件到底是基本圖像元素,還是複雜圖像元素,最後是實現它:



最後附上此文章的結構圖說明:





在這文章,我盡量用比較簡單的方式去解說,當然上面的例子在網址請Google大神來搜尋的話,真的是一大堆,我用自已理解的方式再轉變成我的表達方式。相互激撞可以有不同的火花XDDDDDD。


-雲遊山水為知已逍遙一生而忘齡- 電腦神手


沒有留言:

張貼留言