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

C# 設計模式無廢話系列(二十):Mediator 中介者模式

     編輯:關於C#

意圖

以一個中介對象封裝一組對象的交互,使各對象無需顯式相互引用,達到鬆散耦合,且能獨立變更交互行為。

適用場景

網路遊戲多大區架構:每大區為一組伺服器,擁有獨立資料庫。新需求:玩家資訊跨大區共享(註冊同步、充值通知、消費同步)。

若無中介者,A、B、C 三大區需兩兩互呼叫,耦合極高。引入 AccountSystem 作為中介者:

  • 各大區僅與 AccountSystem 對話,無需知曉其他大區存在。
  • 調整交互策略(如僅 A、B 共享)只需改 AccountSystem。
  • 新增大區無需修改既有大區,統一由 AccountSystem 協調。

通訊流程對比

引入前:網站呼叫 A/B/C 註冊 → A 充值呼叫 B/C → B 充值呼叫 A/C → …(N² 複雜度)

引入後:

  1. 網站呼叫 AccountSystem 註冊
  2. AccountSystem 遍歷呼叫各大區註冊
  3. 各大區充值/消費呼叫 AccountSystem
  4. AccountSystem 遍歷呼叫各大區專屬充值/消費方法

步驟 1-2 即為 Facade 模式;加上 3-6 雙向協調,AccountSystem 成為 Mediator。

完整範例程式碼

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

namespace MediatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            AccountSystem accountSystem = new AccountSystem();
            GameSystem gameArea1 = new GameArea1(accountSystem);
            GameSystem gameArea2 = new GameArea2(accountSystem);
            accountSystem.RegisterGameArea(gameArea1);
            accountSystem.RegisterGameArea(gameArea2);

            string userName = "aaa";
            accountSystem.CreateAccount(userName);
            gameArea1.Recharge(userName, 200);
            gameArea2.Consume(userName, 50);
            accountSystem.QueryBalance(userName);
        }
    }

    class AccountSystem
    {
        Dictionary<string, int> userBalance = new Dictionary<string, int>();
        List<GameSystem> gameAreaList = new List<GameSystem>();

        public void RegisterGameArea(GameSystem gs) => gameAreaList.Add(gs);

        public void CreateAccount(string userName)
        {
            userBalance[userName] = 0;
            foreach (var gs in gameAreaList) gs.CreateAccountSelf(userName);
        }

        public void Recharge(string userName, int amount)
        {
            if (!userBalance.ContainsKey(userName)) return;
            bool ok = true;
            foreach (var gs in gameAreaList) ok &= gs.RechargeSelf(userName, amount);
            if (ok) userBalance[userName] += amount;
        }

        public void Consume(string userName, int amount)
        {
            if (!userBalance.ContainsKey(userName)) return;
            bool ok = true;
            foreach (var gs in gameAreaList) ok &= gs.ConsumeSelf(userName, amount);
            if (ok) userBalance[userName] -= amount;
        }

        public void QueryBalance(string userName) => Console.WriteLine($"Your balance is {userBalance[userName]}");
    }

    abstract class GameSystem
    {
        protected AccountSystem accountSystem;
        protected Dictionary<string, int> userBalance = new Dictionary<string, int>();

        protected GameSystem(AccountSystem accountSystem) => this.accountSystem = accountSystem;

        internal virtual bool CreateAccountSelf(string userName)
        {
            userBalance[userName] = 0; return true;
        }
        internal virtual bool RechargeSelf(string userName, int amount)
        {
            if (userBalance.ContainsKey(userName)) userBalance[userName] += amount;
            return true;
        }
        internal virtual bool ConsumeSelf(string userName, int amount)
        {
            if (userBalance.ContainsKey(userName)) userBalance[userName] -= amount;
            return true;
        }

        public void Recharge(string userName, int amount) => accountSystem.Recharge(userName, amount);
        public void Consume(string userName, int amount) => accountSystem.Consume(userName, amount);
    }

    class GameArea1 : GameSystem
    {
        public GameArea1(AccountSystem a) : base(a) { }
        internal override bool CreateAccountSelf(string userName)
        {
            Console.WriteLine($"{userName} Registered in GameArea1");
            return base.CreateAccountSelf(userName);
        }
        internal override bool RechargeSelf(string userName, int amount)
        {
            base.RechargeSelf(userName, amount);
            Console.WriteLine($"{userName}'s amount in GameArea1 is {userBalance[userName]}");
            return true;
        }
        internal override bool ConsumeSelf(string userName, int amount)
        {
            base.ConsumeSelf(userName, amount);
            Console.WriteLine($"{userName}'s amount in GameArea1 is {userBalance[userName]}");
            return true;
        }
    }

    class GameArea2 : GameSystem
    {
        public GameArea2(AccountSystem a) : base(a) { }
        internal override bool CreateAccountSelf(string userName)
        {
            Console.WriteLine($"{userName} Registered in GameArea2");
            return base.CreateAccountSelf(userName);
        }
        internal override bool RechargeSelf(string userName, int amount)
        {
            base.RechargeSelf(userName, amount);
            Console.WriteLine($"{userName}'s amount in GameArea2 is {userBalance[userName]}");
            return true;
        }
        internal override bool ConsumeSelf(string userName, int amount)
        {
            base.ConsumeSelf(userName, amount);
            Console.WriteLine($"{userName}'s amount in GameArea2 is {userBalance[userName]}");
            return true;
        }
    }
}

程式碼執行結果

Mediator 模式執行結果

程式碼解析

  • AccountSystem:Mediator 角色,內部維護同事清單 gameAreaList。
  • 註冊、充值、消費方法會遍歷同事並呼叫其專屬方法(xxxSelf),全部成功後才更新自身帳戶餘額。
  • GameSystem:抽象同事。提供兩組方法:對外公開的 Recharge/Consume 僅轉發給 Mediator;供 Mediator 呼叫的 xxxSelf 執行實際邏輯。
  • GameArea1/2:具體同事,建構子傳入 Mediator 建立關聯。
  • 同事主動感知 Mediator,操作時僅與 Mediator 通訊,不再直接關聯其他同事。

何時採用

當一組介面相對穩定的對象之間依賴關係錯綜複雜、難以維護或可能變動時,考慮 Mediator。

實作要點

  • C# 中可用 delegate 解耦 Mediator 與同事的交互行為。
  • Mediator vs Observer:前者處理多對多雜亂交互的統籌,後者處理一(多)對多的靈活訂閱。本例後半階段(分散處理)可套用 Observer。
  • Mediator vs Facade:前者同事需雙向通訊,用於子系統間;後者傾向單向通訊,用於子系統與上層系統。本例同時呈現兩者特徵。
  • 架構層常與 Observer、Facade 協用,有時退化為其中之一。重點在於從中獲益,而非名稱定位。

注意事項

  • 非所有系統都需 Mediator 將多對多轉為多對一對多。若同事關聯原本清晰或不複雜,無需強行套用。
  • 實務中 Mediator 邏輯往往複雜,若伴隨需求變更可能難以維護,此時可對 Mediator 角色或處理行為套用其他模式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved