옵저버 패턴

객체 사이에 일 대 다의 의존 관계를 정의해두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지 받고 자동으로 업데이트될 수 있게 만듭니다.

옵저버 패턴이란, 객체의 상태 변화를 관찰하는 관찰자들, 옵저버의 목록을 객체로 등록하여 상태 변화가 있을 때 마다 메서드를 통해 객체가 직접 옵저버에게 통지하는 디자인 패턴이다.

옵저버 패턴이란?

정리 하다 보니 이벤트 주도적 프로그래밍(이벤트 기반 프로그래밍)에서 사용되는 디자인 패턴이라는 걸 쉽게 알 수 있다.

이벤트 주도적 프로그래밍

앞 서 포스팅한 이벤트 주도적 프로그래밍의 내용과 같이 이벤트 관리 매니저를 두고 다른 리스너를 등록 또는 삭제하여 이벤트가 발생할 때 마다 모든 리스너들에게 통지해준다.

위 포스팅에서는 인터페이스를 활용하여 싱글톤으로 매니저를 관리하여 각 객체가 직접 리스너가 되어서 쉽게 전달받는 형태로 만들었다.

조금 알아보면 구현 방법이 정말 다양하다.

또한, MVC패턴(Model-View-Controller)구조는 정말 흔한 구조인데 기반이 되는 패턴이 관찰자 패턴이다.

패턴이 잘 감이 오지 않는다면 모든 상호작용을 update에서 처리하게 되면 엄청난 검사와 가독성이 매우 떨어지게 되는데 특정 값이 변경될 때(HP가 변경될 때 UI가 변경)처럼 이벤트를 등록하고 다른 리스너에게도 통지해주는 형식이다.

C#에는 IObserver, IObservable 옵저버 패턴에 사용되는 인터페이스가 제공된다.

유니티에서는 Unity Event를 열어두고 거기에 필요한 기능 사운드, 이펙트 재생등을 열어두거나 이벤트 기반 프로그래밍의 기본이 된다.

유니티는 애니메이션의 event, Unity Event이외에도 툴만 잘 살펴봐도 옵저버를 사용한 이벤트가 많다.

C#은 delegate, Event, -> action, func등 다양한 기능자체를 지원한다.

따라서 적잘하게 하나의 subject를 가지는 것 보다 하나의 변수처럼 관리하는게 효율이 더 좋다.

C#의 인터페이스를 활용한 구현

namespace ObserverPattern
{
    public interface Observer
    {
        public void OnNotify();
    }

    public interface ISubject
    {
        void AddObserver(Observer observer);
        void RemoveObserver(Observer observer);
        void Notify();
    }

    public class SbscriberA : Observer
    {
        public void OnNotify()
        {
            System.Console.WriteLine("A입니다.");
        }
    }

    public class SbscriberB : Observer
    {
        public void OnNotify()
        {
            System.Console.WriteLine("B입니다.");
        }
    }

    public class Entertainer : ISubject
    {

        List<Observer> observers = new List<Observer>();

        public void AddObserver(Observer observer)
        {
            observers.Add(observer);    
        }

        public void RemoveObserver(Observer observer)
        {
            observers.Remove(observer);
        }

        public void Notify()
        {
            foreach (var observer in observers)
            {
                observer.OnNotify();
            }
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Entertainer entertainer = new Entertainer();

            entertainer.AddObserver(new SbscriberA());
            entertainer.AddObserver(new SbscriberB());
            
            Observer observer = new SbscriberA();
            entertainer.AddObserver(observer);

            entertainer.Notify();

            entertainer.RemoveObserver(observer);
            entertainer.Notify();
        }
    }
}

델리게이트를 활용한 예제

namespace ObserverPattern
{
    delegate void OnEvent();

    class DelegateEntertainer
    {
        public event OnEvent myEvent;
        public void AddObserver(OnEvent sbscriber)
        {
            myEvent += sbscriber;
        }

        public void RemoveObserver(OnEvent sbscriber)
        {
            myEvent -= sbscriber;
        }

        public void Notify()
        {
            myEvent();
        }
    }
}

물론 델리게이트를 클래스에 넣어서 사용이 가능하지만.. 안전성 문제로 외부에서 호출할 수 없는 이벤트를 사용한다.


이 글은 게임 프로그래밍 패턴(로버트 나이스트롬 지음/ 박일 옮김)을 참고 하였습니다.

댓글남기기