EventAggregator

๊ฐœ์š”

EventAggregator๋Š” ์ด๋ฒคํŠธ์˜ ๊ตฌ๋…(Subscribe)๊ณผ ์ถœํŒ(Publish)์˜ ๋งค์ปค๋‹ˆ์ฆ˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ์—์š”.์ด ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฒคํŠธ๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ์ƒ๋Œ€๋ฐฉ์˜ ์ง์ ‘์ ์ธ ์ฐธ์กฐ ์—†์ด ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค„ ์ˆ˜ ์žˆ์„๊บผ์—์š”.

๊ตฌ์กฐ ์•Œ์•„๋ณด๊ธฐ

public interface IEventAggregator;
public interface IApplicationEvent;
public class EventAggregator : IEventAggregator;

๋จผ์ € ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์•Œ์•„๋ณด๊ธฐ๋กœ ํ•ด์š”. ์ด ํŒจํ„ด์€ ์ด๋ฃจ๋Š” ์„ธ ๊ฐœ์˜ ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ์–ด์š”. ์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•์€ ์กฐ๊ธˆ ํ›„์— ์•Œ์•„๋ณด๊ธฐ๋กœ ํ•˜๊ณ  ํ•˜๋‚˜์”ฉ ์„ค๋ช…ํ•ด ๋ณผ๊ป˜์š”.

public interface IEventAggregator 
{
        void Subscribe<T>(Action<T> action) where T : IApplicationEvent;
        void Unsubscribe<T>(Action<T> action) where T : IApplicationEvent;
        void Publish<T>(T message) where T : IApplicationEvent;
}

IEventAggregator ์ธํ„ฐํŽ˜์ด์Šค๋Š”

  • ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋Š” Subscribe

  • ๋“ฑ๋กํ•œ ์ด๋ฒคํŠธ๋ฅผ ํ•ด์ง€ ํ•˜๋Š” Unsubscribe

  • ๋“ฑ๋ก๋œ ์ด๋ฒคํŠธ๋“ค์—๊ฒŒ ๋ฉ”์„ธ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋Š” Publish ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ์–ด์š”.

public interface IApplicationEvent
{
}

IApplicationEvent ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์‹ค์ œ๋กœ ์•„๋ฌด ์ฝ”๋“œ๋„ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋ฐ, ์œ„ IEventAggregator ์˜ where ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ฑธ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ์–ด์š”.

ํ˜•์‹ ์ œ์•ฝ์€ EventAggregator๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฒคํŠธ์™€ ์•„๋‹Œ ์ด๋ฒคํŠธ๋ฅผ ๋‚˜๋ˆ„๋Š”๋ฐ ์œ ์šฉํ•ด์š”.

public class EventAggregator : IEventAggregator
{
        private readonly ConcurrentDictionary<Type, List<object>> subscriptions =
        new ConcurrentDictionary<Type, List<object>>();

    public static IEventAggregator Instance { get; } = new EventAggregator();

    public void Publish<T>(T message) where T : IApplicationEvent
    {
        List<object> subscribers;
        if (subscriptions.TryGetValue(typeof(T), out subscribers))
        {
            // To Array creates a copy in case someone unsubscribes in their own handler
            foreach (var subscriber in subscribers.ToArray())
            {
                ((Action<T>) subscriber)(message);
            }
        }
    }

    public void Subscribe<T>(Action<T> action) where T : IApplicationEvent
    {
        var subscribers = subscriptions.GetOrAdd(typeof(T), t => new List<object>());
        lock (subscribers)
        {
            subscribers.Add(action);
        }
    }

    public void Unsubscribe<T>(Action<T> action) where T : IApplicationEvent
    {
        List<object> subscribers;
        if (subscriptions.TryGetValue(typeof(T), out subscribers))
        {
            lock (subscribers)
            {
                subscribers.Remove(action);
            }
        }
    }

    public void Dispose()
    {
        subscriptions.Clear();
    }
}

์‹ค์ œ ํด๋ž˜์Šค๋Š” ์œ„์™€ ๊ฐ™์ด ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ์— ์•ˆ์ „ํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์–ด์š”. ConcurrentDictionary๋Š” ํ˜•์‹์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” List ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”. ๊ทธ๋ฆฌ๊ณ  Aggregator ํด๋ž˜์Šค๋Š” ์‹ฑ๊ธ€ํ„ด ์ธ์Šคํ„ด์Šค๋กœ ํ™œ์šฉํ–ˆ์–ด์š”. ์ด๋ฅผ ํ†ตํ•ด ์–ด๋””์„œ๋“  ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์–ด์š”. ๋งˆ์ง€๋ง‰ ํด๋ž˜์Šค์˜ ์‚ฌ์šฉ ํ•ด์ œ์‹œ์—” Dispose๋ฅผ ํ†ตํ•ด Dictionary๋ฅผ ์ดˆ๊ธฐํ™” ํ–ˆ์–ด์š”.

์ด๋ฒคํŠธ ๊ตฌ๋…

์ด๋ฒคํŠธ์˜ ๊ตฌ๋…์€ ๋จผ์ € ๋Œ€์ƒ์ด ๋˜๋Š” ์ด๋ฒคํŠธ์˜ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•ด์•ผํ•ด์š”. ์ €๋Š” ์—ฌ๊ธฐ์—์„œ ์˜ˆ์‹œ๋กœ ์ด๋ฒคํŠธ ํด๋ž˜์Šค์™€ ๊ทธ ์ด๋ฒคํŠธ์˜ arguments๋ฅผ ๋‹ค๋ฃจ๋Š” ํด๋ž˜์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ๋ณผ๊ป˜์š”.

public class ProcessCompleteEvent : IApplicationEvent
{
   private ProcessCompleteEventArgs args;
   
   public ProcessCompletedEvent(ProcessCompleteEventArgs args)
   {
      this.args = args;
   }
}

public class ProcessCompleteEventArgs
{
   public int result = default(int);
   public string message = default(string);
   
   public ProcessCompleteEventArgs(int result, string message)
   {
      this.result = result;
      this.message = message;
   }
}

๋Œ€์ƒ์ด ๋  ํด๋ž˜์Šค์™€ ์ด๋ฒคํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด, ์ด์ œ ๊ตฌ๋…์„ ํ•  ์ฐจ๋ก€์—์š”. ๊ตฌ๋…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์–ด์š”.

๋จผ์ € ์ด๋ฒคํŠธ์˜ ํ˜ธ์ถœ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ๋ฉ”์„œ๋“œ๋ฅผ Subscribe ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์š”.

private void OnEventMethod(ProcessCompleteEvent e)
{
    Console.WriteLine(MethodBase.GetCurrentMethod().Name.ToString());
}

EventAggregator.Instance.Subscribe<ProcessCompleteEvent>(OnEventMethod);

์ด๋ฒคํŠธ ์ „๋‹ฌํ•˜๊ธฐ

์ด๋ ‡๊ฒŒ ์ด๋ฒคํŠธ๋ฅผ ์œ„ํ•œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ์ž‘์—…์ด ๋๋‚˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ์ด๋ฒคํŠธ๋ฅผ publish ํ•ฉ๋‹ˆ๋‹ค.

var args = new ProcessCompleteEvent(0, "Test Event Call");

EventAggregator.Instance.Publish<ProcessCompleteEvent>(args);

Last updated