首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 設計模式無廢話系列(十一):Composite 組合模式

     編輯:關於C#

意圖

將對象組合成樹形結構以表示「部分-整體」層次,使客戶端對單個對象與組合對象使用具有一致性。

適用場景

遊戲大區→伺服器→服務 三層結構。管理工具需顯示所有資訊、啟動服務(單服務/整伺服器/整大區)。若客戶端直接依賴各層,如總裁既管總監也管底層員工,過於繁瑣。引入 Composite 統一介面。

完整範例程式碼

using System;
using System.Collections.Generic;
using System.Text;

namespace CompositeExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Element server1 = new GameServer("GS1", "192.168.0.1");
            server1.Add(new GameService("Lobby1", 1, "S5Lobby1"));
            server1.Add(new GameService("Gate1", 2, "S5Gate1"));
            server1.Add(new GameService("DataExchange1", 3, "S5DataExchange1"));
            server1.Add(new GameService("Rank1", 4, "S5Rank1"));
            server1.Add(new GameService("Log1", 5, "S5Log1"));

            Element server2 = new GameServer("GS2", "192.168.0.2");
            server2.Add(new GameService("Lobby2", 1, "S5Lobby2"));
            server2.Add(new GameService("Gate2", 2, "S5Gate2"));
            server2.Add(new GameService("DataExchange2", 3, "S5DataExchange1"));
            server2.Add(new GameService("Rank2", 4, "S5Rank2"));
            server2.Add(new GameService("Log2", 5, "S5Log2"));

            Element area = new GameArea("電信區");
            area.Add(server1);
            area.Add(server2);
            area.Display();
            area.Start();
            area.Stop();
        }
    }
    abstract class Element
    {
        protected string name;
        public Element(string name) { this.name = name; }
        public abstract void Add(Element element);
        public abstract void Remove(Element element);
        public abstract void Display();
        public abstract void Start();
        public abstract void Stop();
    }
    class GameService : Element, IComparable<GameService>
    {
        int serviceType; string serviceName;
        public GameService(string name, int type, string sName) : base(name) { serviceType = type; serviceName = sName; }
        public override void Add(Element e) => throw new ApplicationException("Leaf cannot add");
        public override void Remove(Element e) => throw new ApplicationException("Leaf cannot remove");
        public override void Display() => Console.WriteLine($"name:{Name},serviceType:{serviceType},serviceName:{serviceName}");
        public override void Start() => Console.WriteLine($"{Name} started");
        public override void Stop() => Console.WriteLine($"{Name} stopped");
        public int CompareTo(GameService other) => other.serviceType.CompareTo(serviceType);
    }
    class GameServer : Element
    {
        string serverIP; List<GameService> serviceList = new List<GameService>();
        public GameServer(string name, string ip) : base(name) { serverIP = ip; }
        public override void Add(Element e) => serviceList.Add((GameService)e);
        public override void Remove(Element e) => serviceList.Remove((GameService)e);
        public override void Display()
        {
            Console.WriteLine($"{new string('+',10)}{Name}({serverIP}){new string('+',10)}");
            foreach (var e in serviceList) e.Display();
        }
        public override void Start()
        {
            serviceList.Sort();
            Console.WriteLine($"=============Starting the whole {Name}=============");
            for (int i = 0; i < serviceList.Count; i++) serviceList[i].Start();
            Console.WriteLine($"=============The whole {Name} started=============");
        }
        public override void Stop()
        {
            Console.WriteLine($"=============Stopping the whole {Name}=============");
            for (int i = serviceList.Count - 1; i >= 0; i--) serviceList[i].Stop();
            Console.WriteLine($"=============The whole {Name} stopped=============");
        }
    }
    class GameArea : Element
    {
        List<GameServer> serverList = new List<GameServer>();
        public GameArea(string name) : base(name) { }
        public override void Add(Element e) => serverList.Add((GameServer)e);
        public override void Remove(Element e) => serverList.Remove((GameServer)e);
        public override void Display()
        {
            Console.WriteLine(new string('=',20));
            Console.WriteLine($" {Name}");
            Console.WriteLine(new string('=',20));
            foreach (var e in serverList) e.Display();
        }
        public override void Start()
        {
            Console.WriteLine($"=============Starting the whole {Name}=============");
            foreach (var e in serverList) e.Start();
            Console.WriteLine($"=============The whole {Name} started=============");
        }
        public override void Stop()
        {
            Console.WriteLine($"=============Stopping the whole {Name}=============");
            foreach (var e in serverList) e.Stop();
            Console.WriteLine($"=============The whole {Name} stopped=============");
        }
    }
}

程式碼執行結果

Composite 模式執行結果

程式碼解析

  • Element:抽象構件,提供一致介面(Add/Remove/Display/Start/Stop)。
  • 透明 vs 安全:介面含 Add/Remove 為透明模式(葉節點執行拋異常),移除則為安全模式(葉節點無此介面)。視需求取捨。
  • GameService:葉構件。Add/Remove 拋異常。實作 IComparable 供排序(按服務類型)。
  • GameServer:分支構件。管理服務清單。Start 依類型排序後按序啟動(日誌→排名→數據交換→大廳);Stop 逆序關閉。體現分支節點的管理邏輯不只是增刪。
  • GameArea:頂層分支,管理伺服器清單。

何時採用

  • 代碼角度:類別形成層次結構,希望統一介面管理各層。
  • 應用角度:希望將一對多關係轉為一對一關係。

實作要點

  • 透明/安全模式視需求定。
  • 葉節點有時需訪問分支獲取資訊。
  • 葉節點多、分支頻繁遍歷時考慮快取。
  • 客戶端應針對抽象構件編程。

注意事項

  • 無特殊注意事項。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved