EventAggregator

EventAggregator 패턴이란?

EventAggregator 패턴은 객체 간의 느슨한 결합loose coupling을 유지하면서, 이벤트를 중앙에서 관리할 수 있도록 설계된 행동 패턴입니다.
특히, 발행-구독Pub-Sub, Publish-Subscribe 패턴의 변형으로, 이벤트를 발행하는 객체와 구독하는 객체 간의 의존성을 제거합니다.

EventAggregator 패턴 구조

D2 Diagram

  • Client → Publisher
    • 클라이언트는 이벤트를 발행합니다.
  • Publisher → EventAggregator
    • 발행자는 EventAggregator에게 이벤트를 전달합니다.
  • Client → EventAggregator
    • 클라이언트는 이벤트를 구독합니다.
  • EventAggregator → Subscriber
    • EventAggregator는 이벤트를 구독한 객체들에게 이벤트를 전달합니다.

EventAggregator 패턴 적용

객체 간 이벤트 전달을 직접적으로 구현하면, 아래와 같은 문제가 발생할 수 있습니다.

잘못된 처리

public class Publisher
{
    private readonly Subscriber _subscriber;
    public Publisher(Subscriber subscriber)
    {
        _subscriber = subscriber;
    }
    public void Publish(string message)
    {
        _subscriber.Receive(message); // 직접 호출로 강결합
    }
}
public class Subscriber
{
    public void Receive(string message)
    {
        Console.WriteLine($"Subscriber received: {message}");
    }
}

높은 결합도

  • PublisherSubscriber의 구체적인 구현Receive에 직접 의존하고 있습니다.
  • 구독자를 추가하거나 수정하려면 Publisher의 코드를 변경해야 하므로, 확장에 취약합니다. (개방_폐쇄 원칙 위반)

직접적인 호출로 인해 유연성 부족

  • PublisherSubscriber를 직접 호출하므로, 다양한 타입의 Subscriber를 처리하기 어렵습니다.

단일 책임 원칙 위반

  • Publisher 클래스가 이벤트 발행뿐만 아니라 구독자 관리의 책임까지 맡고 있습니다.
  • 구독자 목록 관리와 이벤트 발행이 같은 클래스에 있어 책임이 분리되지 않았습니다.

동기 호출로 인한 성능 문제

  • 이벤트를 발행할 때 모든 구독자에게 동기적으로 전달됩니다.
  • 구독자 수가 많거나, 구독자의 처리 로직이 오래 걸릴 경우 전체 시스템의 성능이 저하됩니다.

EventAggregator 패턴 적용 예시

EventAggregator 클래스

using System;
using System.Collections.Generic;
public class EventAggregator
{
    private readonly Dictionary<Type, List<Delegate>> _subscribers = new();
    // 구독 등록
    public void Subscribe<T>(Action<T> subscriber)
    {
        if (!_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = new List<Delegate>();
        }
        _subscribers[typeof(T)].Add(subscriber);
    }
    // 이벤트 발행
    public void Publish<T>(T eventData)
    {
        if (_subscribers.ContainsKey(typeof(T)))
        {
            foreach (var subscriber in _subscribers[typeof(T)])
            {
                ((Action<T>)subscriber)(eventData);
            }
        }
    }
    // 구독 해제
    public void Unsubscribe<T>(Action<T> subscriber)
    {
        if (_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)].Remove(subscriber);
        }
    }
}

Publisher 클래스

public class Publisher
{
    private readonly EventAggregator _eventAggregator;
    public Publisher(EventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }
    public void PublishEvent(string message)
    {
        Console.WriteLine($"Publisher: Publishing message '{message}'");
        _eventAggregator.Publish(message);
    }
}

Subscriber 클래스

public class Subscriber
{
    public Subscriber(EventAggregator eventAggregator)
    {
        eventAggregator.Subscribe<string>(HandleEvent);
    }
    private void HandleEvent(string message)
    {
        Console.WriteLine($"Subscriber: Received message '{message}'");
    }
}

클라이언트 코드

class Program
{
    static void Main(string[] args)
    {
        var eventAggregator = new EventAggregator();
        // Subscriber 등록
        var subscriber1 = new Subscriber(eventAggregator);
        var subscriber2 = new Subscriber(eventAggregator);
        // Publisher를 통해 이벤트 발행
        var publisher = new Publisher(eventAggregator);
        publisher.PublishEvent("Hello, EventAggregator!");
    }
}

EventAggregator 패턴의 장단점

EventAggregator의 장점

  • 낮은 결합도: 발행자와 구독자는 서로를 알 필요 없이 EventAggregator를 통해 통신합니다.
  • 유연성: 새로운 이벤트와 구독자를 쉽게 추가할 수 있습니다.
  • 중앙 집중식 관리: 이벤트 흐름을 EventAggregator에서 관리하여 코드 가독성과 유지보수성이 향상됩니다.
  • 성능 최적화 가능성: 비동기 호출, 스레드 분리 등으로 확장하여 성능을 더욱 최적화할 수 있습니다.
  • 유지보수성 향상: 책임이 분리되어 각 클래스가 본연의 역할에만 집중하므로, 코드의 가독성과 유지보수성이 크게 향상되었습니다.

EventAggregator의 단점과 해결방안

디버깅 어려움

  • 이벤트 발행자와 구독자 간의 관계가 간접적이므로, 특정 이벤트가 어디서 발행되고 누구에게 전달되는지 추적하기 어렵습니다.
  • 특히, 이벤트가 예상하지 못한 객체에 전달될 때 디버깅이 복잡해질 수 있습니다.
  • 해결 방안:
    • 로깅을 추가하여 이벤트 발행과 구독 활동을 기록합니다.
    • 디버깅 도구를 활용해 이벤트 흐름을 시각화합니다.
    • 이벤트 이름 또는 타입을 명확하게 정의하여 추적 가능성을 높입니다.
public class EventAggregator
{
    public void Publish<T>(T eventData)
    {
        Console.WriteLine($"Event Published: {typeof(T).Name}");
        // 이벤트 발행 로직...
    }
}

메모리 누수

  • 이벤트를 구독한 객체가 구독을 해제하지 않으면, EventAggregator가 해당 객체를 참조하게 되어 메모리 누수가 발생할 수 있습니다.
  • 해결 방안:
    • 약한 참조WeakReference를 사용하여 구독자 객체를 관리합니다.
    • 구독 객체에서 구독 해제를 명시적으로 처리하도록 설계합니다.
    • IDisposable 인터페이스를 구현하여 구독 해제를 자동화합니다.

남용 가능성

  • 모든 통신을 EventAggregator를 통해 처리하면, 시스템이 과도하게 이벤트 중심으로 설계되어 복잡도가 증가합니다.
  • 해결 방안:
    • 적절한 범위에서 사용하도록 가이드라인을 작성합니다.
    • 단순한 객체 간 통신에는 직접 호출 또는 Mediator 패턴을 사용합니다.
    • 이벤트 기반 설계는 꼭 필요한 곳에서만 사용합니다.

성능 문제

  • 이벤트 발행과 전달이 빈번할 경우, 성능 병목이 발생할 수 있습니다.
  • 해결 방안:
    • 비동기 처리를 도입하여 이벤트를 병렬로 처리합니다.
    • 이벤트 전달 주기를 제한하는 Throttle 또는 Debounce 기법을 적용합니다.

맺음말

EventAggregator 패턴은 중앙 집중식 이벤트 관리를 통해 객체 간 결합도를 낮추고, 복잡한 이벤트 흐름을 효율적으로 처리할 수 있는 강력한 도구입니다. 특히, MVVM과 같은 패턴에서 ViewModel 간 통신을 처리하는 데 매우 유용합니다.

EventAggregator 우선순위 처리

EventAggregator는 발행-구독Pub-Sub 모델을 기반으로 객체 간의 느슨한 결합을 구현하지만, 기본적으로 구독자Subscriber들은 발행된 이벤트를 등록된 순서대로 처리합니다. 그러나 특정 구독자의 처리 순서를 우선적으로 지정해야 하는 경우가 발생할 수 있습니다. 이를 해결하기 위해 우선순위 처리priority handling 기능을 추가합니다.

왜 우선순위 처리가 필요한가?

중요 작업의 우선 처리

  • 특정 이벤트가 발생했을 때, 중요도가 높은 구독자가 먼저 실행되어야 할 수 있습니다.
  • 예: 결제 시스템에서 데이터 검증 로직이 먼저 실행된 후 로그 저장 로직이 실행되어야 함.

구독자 간의 의존 관계 처리

  • 어떤 구독자의 결과가 다른 구독자에게 영향을 미칠 수 있습니다.
  • 예: 데이터 정렬 후 출력 로직이 실행되어야 하는 경우.

시스템 성능 최적화

  • 중요한 작업을 먼저 처리하여 시스템의 응답성을 보장하고, 덜 중요한 작업은 나중에 실행하도록 구성.

EventAggregator 설계 및 구현

우선순위를 지원하기 위해 구독자 등록 시 우선순위priority를 설정하도록 EventAggregator를 확장합니다.

EventSubscription 클래스

EventSubscription은 구독자의 정보를 저장하고 우선순위를 정의합니다.

public class EventSubscription<T>
{
    public Action<T> Subscriber { get; }
    public int Priority { get; }
    public EventSubscription(Action<T> subscriber, int priority)
    {
        Subscriber = subscriber;
        Priority = priority;
    }
}

확장된 EventAggregator

  • 구독자를 우선순위에 따라 저장하고, 이벤트 발행 시 우선순위를 기준으로 호출 순서를 정합니다.
using System;
using System.Collections.Generic;
using System.Linq;
public class EventAggregator
{
    private readonly Dictionary<Type, List<object>> _subscribers = new();
    // 구독 등록 (우선순위 설정)
    public void Subscribe<T>(Action<T> subscriber, int priority = 0)
    {
        var subscription = new EventSubscription<T>(subscriber, priority);
        if (!_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = new List<object>();
        }
        _subscribers[typeof(T)].Add(subscription);
    }
    // 이벤트 발행
    public void Publish<T>(T eventData)
    {
        if (!_subscribers.ContainsKey(typeof(T)))
            return;
        var subscriptions = _subscribers[typeof(T)]
            .Cast<EventSubscription<T>>() // 타입 변환
            .OrderByDescending(s => s.Priority); // 우선순위 내림차순 정렬
        foreach (var subscription in subscriptions)
        {
            subscription.Subscriber(eventData);
        }
    }
    // 구독 해제
    public void Unsubscribe<T>(Action<T> subscriber)
    {
        if (_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = _subscribers[typeof(T)]
                .Cast<EventSubscription<T>>()
                .Where(s => s.Subscriber != subscriber)
                .Cast<object>()
                .ToList();
        }
    }
}

Subscriber 클래스

구독자가 등록될 때 우선순위를 명시하여 설정합니다.

public class Subscriber
{
    public Subscriber(EventAggregator eventAggregator)
    {
        // 높은 우선순위(10)로 등록
        eventAggregator.Subscribe<string>(HandleHighPriorityEvent, priority: 10);
        // 낮은 우선순위(1)로 등록
        eventAggregator.Subscribe<string>(HandleLowPriorityEvent, priority: 1);
    }
    private void HandleHighPriorityEvent(string message)
    {
        Console.WriteLine($"[High Priority] Received: {message}");
    }
    private void HandleLowPriorityEvent(string message)
    {
        Console.WriteLine($"[Low Priority] Received: {message}");
    }
}

Publisher 클래스

Publisher는 단순히 이벤트를 발행하며, 구독자의 우선순위는 신경 쓰지 않습니다.

public class Publisher
{
    private readonly EventAggregator _eventAggregator;
    public Publisher(EventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }
    public void PublishEvent(string message)
    {
        Console.WriteLine($"Publisher: Publishing message '{message}'");
        _eventAggregator.Publish(message);
    }
}

클라이언트 코드

class Program
{
    static void Main(string[] args)
    {
        var eventAggregator = new EventAggregator();
        // Subscriber 등록
        var subscriber = new Subscriber(eventAggregator);
        // Publisher를 통해 이벤트 발행
        var publisher = new Publisher(eventAggregator);
        publisher.PublishEvent("Hello, EventAggregator!");
    }
}

출력 결과

Publisher: Publishing message 'Hello, EventAggregator!'
[High Priority] Received: Hello, EventAggregator!
[Low Priority] Received: Hello, EventAggregator!

확장 가능성

우선순위 그룹화

  • 동일 우선순위 내에서도 그룹별로 호출 순서를 설정할 수 있습니다.

우선순위 동적 변경

  • 구독자의 우선순위를 동적으로 변경하여 이벤트 처리 순서를 재조정할 수 있습니다.

우선순위와 동기/비동기 결합

  • 우선순위를 지원하면서 동기/비동기 호출 방식을 추가로 구현하여 성능 최적화를 도모할 수 있습니다.

동기/비동기 처리

EventAggregator는 객체 간의 느슨한 결합을 지원하여 발행-구독Pub-Sub 모델을 구현하지만, 기본적으로 동기적으로 동작합니다. 이벤트를 발행하면 모든 구독자가 즉시 호출되어야 하며, 구독자 호출이 완료되기 전까지 발행자는 대기합니다. 그러나 비동기 처리를 추가하면 시스템 성능과 응답성이 향상되고, 특정 구독자의 처리 지연이 전체 이벤트 흐름에 영향을 주는 것을 방지할 수 있습니다.

왜 동기/비동기 처리가 필요한가?

동기 호출의 문제점

  • 이벤트 구독자 중 하나가 시간이 오래 걸리는 작업을 수행하면, 다른 구독자나 발행자까지 지연됩니다.
  • 모든 구독자가 반드시 동기 호출이 필요한 것은 아닙니다.

비동기 호출의 장점

  • 비동기적으로 처리할 수 있는 작업은 별도의 스레드에서 실행하여 메인 스레드의 부담을 줄입니다.
  • UI 스레드에서는 즉시 응답성을 제공하며, 장시간 작업을 백그라운드로 이동합니다.

동기/비동기 처리 병행

  • 중요한 작업은 동기적으로 처리하고, 부수적인 작업(예: 로깅)은 비동기로 처리하는 방식으로 시스템 성능과 안정성을 모두 확보할 수 있습니다.

EventAggregator 설계 및 구현

EventSubscription 클래스

구독자 정보를 저장하며, 동기/비동기 여부를 포함합니다.

public class EventSubscription<T>
{
    public Action<T> Subscriber { get; }
    public bool IsAsync { get; }
    public EventSubscription(Action<T> subscriber, bool isAsync)
    {
        Subscriber = subscriber;
        IsAsync = isAsync;
    }
}

확장된 EventAggregator

  • Subscribe 메서드에서 구독자가 동기/비동기로 호출될지 설정합니다.
  • Publish 메서드에서 동기/비동기 여부에 따라 이벤트를 실행합니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class EventAggregator
{
    private readonly Dictionary<Type, List<object>> _subscribers = new();
    // 구독 등록
    public void Subscribe<T>(Action<T> subscriber, bool isAsync = false)
    {
        var subscription = new EventSubscription<T>(subscriber, isAsync);
        if (!_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = new List<object>();
        }
        _subscribers[typeof(T)].Add(subscription);
    }
    // 이벤트 발행
    public void Publish<T>(T eventData)
    {
        if (!_subscribers.ContainsKey(typeof(T)))
            return;
        var subscriptions = _subscribers[typeof(T)].Cast<EventSubscription<T>>();
        foreach (var subscription in subscriptions)
        {
            if (subscription.IsAsync)
            {
                // 비동기 처리
                Task.Run(() => subscription.Subscriber(eventData));
            }
            else
            {
                // 동기 처리
                subscription.Subscriber(eventData);
            }
        }
    }
    // 구독 해제
    public void Unsubscribe<T>(Action<T> subscriber)
    {
        if (_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = _subscribers[typeof(T)]
                .Cast<EventSubscription<T>>()
                .Where(s => s.Subscriber != subscriber)
                .Cast<object>()
                .ToList();
        }
    }
}

Subscriber 클래스

구독자가 동기/비동기 여부를 설정하여 등록합니다.

public class Subscriber
{
    public Subscriber(EventAggregator eventAggregator)
    {
        // 동기 처리
        eventAggregator.Subscribe<string>(HandleSyncEvent, isAsync: false);
        // 비동기 처리
        eventAggregator.Subscribe<string>(HandleAsyncEvent, isAsync: true);
    }
    private void HandleSyncEvent(string message)
    {
        Console.WriteLine($"[Sync] Received: {message}");
    }
    private void HandleAsyncEvent(string message)
    {
        Console.WriteLine($"[Async] Received: {message}");
    }
}

Publisher 클래스

Publisher는 이벤트 발행만 담당하며, 구독자의 동기/비동기 여부와는 무관합니다.

public class Publisher
{
    private readonly EventAggregator _eventAggregator;
    public Publisher(EventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }
    public void PublishEvent(string message)
    {
        Console.WriteLine($"Publisher: Publishing message '{message}'");
        _eventAggregator.Publish(message);
    }
}

클라이언트 코드

class Program
{
    static void Main(string[] args)
    {
        var eventAggregator = new EventAggregator();
        // Subscriber 등록
        var subscriber = new Subscriber(eventAggregator);
        // Publisher를 통해 이벤트 발행
        var publisher = new Publisher(eventAggregator);
        publisher.PublishEvent("Hello, EventAggregator!");
    }
}

출력 결과

Publisher: Publishing message 'Hello, EventAggregator!'
[Sync] Received: Hello, EventAggregator!
[Async] Received: Hello, EventAggregator!

확장 가능성

모든 비동기 작업 완료 대기

  • 비동기 구독자들이 완료될 때까지 대기하는 PublishAsync 메서드를 추가하여 이벤트 흐름을 동기적으로 관리.
public async Task PublishAsync<T>(T eventData)
{
    if (!_subscribers.ContainsKey(typeof(T)))
        return;
    var tasks = _subscribers[typeof(T)]
        .Cast<EventSubscription<T>>()
        .Select(s => s.IsAsync
            ? Task.Run(() => s.Subscriber(eventData))
            : Task.CompletedTask);
    await Task.WhenAll(tasks);
}

예외 처리

  • 비동기 호출에서 발생하는 예외를 개별적으로 처리하거나 중앙에서 관리.
try
{
    Task.Run(() => subscription.Subscriber(eventData)).Wait();
}
catch (Exception ex)
{
    Console.WriteLine($"Error in async subscriber: {ex.Message}");
}

우선순위와 비동기 처리

  • 모든 구독자가 비동기라면, 우선순위는 무시됩니다.
  • 전략에 따라 우선순위를 먼저 처리하고 나머지를 비동기로 일괄 처리할 수 있습니.

필터링 지원

구독자가 데이터를 수신하기 전에 EventAggregator에서 필터링을 수행하도록 설계할 수도 있습니다. 이 방식은 구독자가 아닌 발행 단계에서 필터링을 추가하는 것입니다.

  • 필터링 로직을 중앙에서 관리하여 구독자의 단순화.
  • 중복된 필터링 로직을 제거할 수 있음.

확장된 EventAggregator

public class EventAggregator
{
    private readonly Dictionary<Type, List<EventSubscription<object>>> _subscribers = new();
    public void Subscribe<T>(Action<object, T> subscriber, Func<T, bool> filter = null)
    {
        var subscription = new EventSubscription<object>((sender, data) => subscriber(sender, (T)data), filter);
        if (!_subscribers.ContainsKey(typeof(T)))
        {
            _subscribers[typeof(T)] = new List<EventSubscription<object>>();
        }
        _subscribers[typeof(T)].Add(subscription);
    }
    public void Publish<T>(object sender, T eventData)
    {
        if (_subscribers.ContainsKey(typeof(T)))
        {
            foreach (var subscription in _subscribers[typeof(T)])
            {
                var sub = subscription as EventSubscription<T>;
                if (sub.Filter == null || sub.Filter(eventData)) // 필터 조건
                {
                    sub.Subscriber(sender, eventData);
                }
            }
        }
    }
}
public class EventSubscription<T>
{
    public Action<object, T> Subscriber { get; }
    public Func<T, bool> Filter { get; }
    public EventSubscription(Action<object, T> subscriber, Func<T, bool> filter = null)
    {
        Subscriber = subscriber;
        Filter = filter;
    }
}

Subscriber

구독 시 필터 조건을 설정합니다.

public class Subscriber
{
    public Subscriber()
    {
        EventAggregator.Instance.Subscribe<string>(HandleEvent, filter: message => message == "Important");
    }
    private void HandleEvent(object sender, string message)
    {
        Console.WriteLine($"Processing important message: {message}");
    }
}

다른 패턴과의 관계

EventAggregator는 객체 간의 느슨한 결합을 유지하면서 이벤트를 중앙에서 관리할 수 있도록 설계된 행동 패턴입니다. 이는 여러 디자인 패턴과 유사하거나 보완적인 역할을 수행하며, 특정 시나리오에서 다른 패턴과 함께 사용될 수 있습니다.

EventAggregator와 Observer

유사점

  • Pub-Sub 모델 기반: ObserverEventAggregator 모두 발행-구독(Publish-Subscribe) 모델을 기반으로 합니다.
  • 상태 변화 통지: 둘 다 객체의 상태 변화 시 구독자에게 알림을 보냅니다.

차이점

특징ObserverEventAggregator
의존성발행자와 구독자가 직접 연결됨발행자와 구독자가 서로 알 필요 없음
중앙 관리발행자가 구독자를 직접 관리EventAggregator가 구독자를 중앙에서 관리
확장성구독자 추가 시 발행자에 영향을 미침구독자 추가/제거가 발행자에 영향을 미치지 않음
사용 규모작은 규모의 이벤트 관리대규모 시스템의 이벤트 흐름 관리

적용 시점

  • Observer는 구독자가 적고, 발행자와 구독자 간의 연결이 명확한 경우에 적합합니다.
  • EventAggregator는 대규모 시스템에서 이벤트 흐름을 중앙 집중식으로 관리해야 할 때 유리합니다.

EventAggregator와 Mediator

유사점

  • 중앙 집중식 관리: MediatorEventAggregator 모두 객체 간의 상호작용을 중앙에서 조정합니다.
  • 결합도 감소: 두 패턴 모두 객체 간의 직접 의존성을 줄여 느슨한 결합을 유지합니다.

차이점

특징MediatorEventAggregator
목적객체 간 상호작용의 조정이벤트 발행 및 전달 관리
중앙 관리의 역할중재자가 논리적 흐름과 로직을 관리EventAggregator는 단순히 이벤트를 전달
다 대 다 통신제한적 (주로 1 대 다 또는 다 대 1)다 대 다 이벤트 통신 가능
복잡성중앙에서 로직을 조정하므로 복잡도가 높음이벤트 관리만 담당하므로 상대적으로 단순함

적용 시점

  • Mediator는 논리적 상호작용을 조정하고, 로직이 포함될 때 적합합니다.
  • EventAggregator는 이벤트 전달만 필요하고, 로직은 구독자가 처리하는 경우에 유리합니다.

EventAggregator와 Command

유사점

  • 명령의 분리: Command 패턴과 마찬가지로 EventAggregator도 요청Event을 분리하여 관리합니다.
  • 확장성: 새로운 명령이나 이벤트를 추가하기 쉽습니다.

차이점

특징CommandEventAggregator
역할요청을 캡슐화하고 실행 로직을 분리이벤트를 전달하고 구독자가 처리
중앙 관리없음EventAggregator가 이벤트 관리
응답 방식요청마다 1개의 응답1개의 요청에 여러 구독자가 응답 가능
명령 추적명령 실행 이력을 추적할 수 있음이벤트의 상태는 추적하지 않음

적용 시점

  • Command는 작업의 캡슐화와 실행 추적이 필요할 때 사용됩니다.
  • EventAggregator는 작업보다는 이벤트 흐름을 관리하는 데 적합합니다.

EventAggregator와 Chain of Responsibility

유사점

  • 다수의 처리자: 여러 구독자가 요청을 처리할 수 있는 구조를 제공합니다.

차이점

특징Chain of ResponsibilityEventAggregator
처리 흐름각 처리자가 요청을 순차적으로 처리구독자가 독립적으로 처리
처리 순서명시적으로 정의된 순서(체인)구독 순서 또는 우선순위에 따름
중앙 집중 관리없음EventAggregator가 구독자 관리

적용 시점

  • Chain of Responsibility는 요청이 특정 순서에 따라 처리되어야 할 때 적합합니다.
  • EventAggregator는 요청이 독립적으로 처리되어야 할 때 적합합니다.

EventAggregator와 MVVM

EventAggregator는 MVVMModel-View-ViewModel 패턴에서 ViewModel 간 통신을 위한 강력한 도구로 사용됩니다. MVVM은 View와 ViewModel을 분리하여 느슨한 결합을 유지하지만, ViewModel 간의 의사소통이 필요할 때 직접적인 참조를 만들면 결합도가 높아지는 문제가 발생할 수 있습니다. 이 문제를 해결하기 위해 EventAggregator를 사용하면 ViewModel 간 통신을 중앙에서 관리하여 MVVM의 원칙을 유지하면서도 효율적인 데이터 교환이 가능합니다.

EventAggregator와 MVVM의 구조

D2 Diagram

  • ViewModel1EventAggregator를 통해 이벤트를 발행합니다.
  • ViewModel2는 해당 이벤트를 구독하여 알림을 받습니다.
  • View는 두 ViewModel과 데이터 바인딩을 통해 연결됩니다.