디자인 패턴/Code 디자인 패턴

[C#] Provider(공급자) - Observer(관찰자) 패턴

로파이 2023. 1. 21. 14:47

Provider - 이벤트를 발생시키면서(제공하면서) Observer를 관리하는 개체

자신이 관리하고 있는 Observer에 대하여 Subscribe하는 방법을 갖고 있고 이벤트 발생 함수를 invoke 한다.

 

Observer - Provider가 발생시키는 이벤트를 구독한다.

Observer는 Provider에 Subscibe 함수로 구독을 실행하고 그 반환 Unsubscribe 클래스 개체를 소유한다.

Observer는 원할 때 Unbsubscribe 개체 인스턴스를 이용하여 구독을 취소한다.

 

C#

Provider는 IObserable<T> 인터페이스를 구현한다.

public interface IObservable<out T>
{
    IDisposable Subscribe(IObserver<T> observer);
}

IDisposable를 반환하는 이유는 구독을 취소하기 위해 Dispose() 함수를 이용하는 개체를 반환하기 때문이다.

T 타입은 주로 Provider가 제공하는 이벤트 타입을 의믜한다.

 

Provider 예시

 

BaggageHandler : IObserable<BaggageInfo>

 

공항에서 출발하거나 도착한 수화물 정보(이벤트)를 제공한다.

수화물 정보(BaggageInfo)는 다음과 같다.

public class BaggageInfo
{
    private int flightNo;
    private string origin;
    private int location;

    internal BaggageInfo(int flight, string from, int carousel)
    {
        flightNo = flight;
        origin = from;
        location = carousel;
    }

    public int FlightNumber
    {
        get { return flightNo; }
    }

    public string From
    {
        get { return origin; }
    }

    public int Carousel
    {
        get { return location; }
    }
}

 

Subscribe 함수를 통해 관찰자를 구독한다. 관찰자가 구독을 취소하는 방법을 반환한다.

public class BaggageHandler : IObservable<BaggageInfo>
{
    private List<IObserver<BaggageInfo>> observers = new List<IObserver<BaggageInfo>>();
    private List<BaggageInfo> flights = new List<BaggageInfo>();

    public BaggageHandler() { }

    public IDisposable Subscribe(IObserver<BaggageInfo> observer)
    {
        // Check whether observer is already registered. If not, add it
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
            // Provide observer with existing data
            foreach (var item in flights)
                observer.OnNext(item);
        }

        return new Unsubscriber<BaggageInfo>(observers, observer);
    }

 

Unscriber는 IDisposable의 구현체로 구독을 취소하는 방법을 제공한다.

internal class Unsubscriber<BaggageInfo> : IDisposable
{
    private List<IObserver<BaggageInfo>> _observers;
    private IObserver<BaggageInfo> _observer;

    internal Unsubscriber(List<IObserver<BaggageInfo>> observers, IObserver<BaggageInfo> observer)
    {
        _observers = observers;
        _observer = observer;
    }

    public void Dispose()
    {
        if (_observers.Contains(_observer))
        {
            _observers.Remove(_observer);
        }
    }
}

 

관찰자 예시

 

ArrivalsMonitor 도착 정보 모니터

IObserver<BaggageInfo>를 구현하여 이벤트 발생 후 이벤트 처리, 완료, 에러 처리를 구현한다.

public interface IObserver<in T>
{
    void OnCompleted(); // 완료 처리
    void OnError(Exception error); // 오류 처리
    void OnNext(T value); // 이벤트 처리
}


ArrivalsMonitor

도착 정보를 관찰하는 관찰자 클래스

public class ArrivalsMonitor : IObserver<BaggageInfo>
{
    private string name;
    private List<string> flightInfos = new List<string>();
    private IDisposable cancellation;
    private string fmt = "{0,-20} {1,5} {2,3}";

    public ArrivalsMonitor(string name)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException("the observer must be assigned a name");
        this.name = name;
    }

    public virtual void Subscribe(IObservable<BaggageInfo> provider)
    {
        cancellation = provider.Subscribe(this);
    }

    public virtual void Unsubscribe()
    {
        cancellation?.Dispose();
        flightInfos.Clear();
    }
	
    public virtual void OnCompleted()
    {
    //	완료 처리
        flightInfos.Clear();
    }

    public virtual void OnError(Exception e)
    {
        // 오류 처리
    }

    public virtual void OnNext(BaggageInfo info)
    {
        // 이벤트 처리
    }
 }

 

전체 코드 참고

https://learn.microsoft.com/ko-kr/dotnet/standard/events/observer-design-pattern-best-practices