HaPpY hApPy

Observer Pattern 옵저버 패턴 본문

DesignPattern (디자인패턴)/Head First Design Pattern

Observer Pattern 옵저버 패턴

juniguya 2013. 7. 26. 11:53


옵저버 패턴,


ObserverPattern.zip


[2013.10.26] 수정 오랜만에 다시 패턴 공부하다가 Push 방법은 있는데 Pull 방법이 없어서 추가!

Push 방법을 먼저 이해하고 Program.cs 파일 읽어보면서 보면 이해 하기 쉽다.

[신문사가 컨텐츠를 구독자에게 Push]

Push 방법은 반 강제적으로 받아야되고(이것도 물론 처리하면되지만, 리소스 낭비가 있을수있다.)


[구독자가 구매자에게 컨텐츠를 요구] (요구하기전에 업데이트된 정보가 있는지 물어볼수도있다.)

Pull 방법은 1. 업데이트된 정보가 있는지 체크, 2. 업데이트 내용을 받음 으로 처리하면 된다.

ObserverPushAndPull.zip



신문사와 구독자 로 이해하면 쉬움


신문사는 Subject


구독자는 Observer


 


신문사에 새로운 소식이 들어오면


Observer로 등록된 구독자들에게 알려줌,


HeadFirst 자바 책 예제를 보면 

(Cinfo 클래스는 내가 직접 작성해서 humidity, pressure, temperture 를 빼서 클래스로 만들었다.)

 class CInfo 
    {
        float _temperture;
        float _humidity;
        float _pressure;

        public float Temperture
        {
            get { return _temperture; }
            set { _temperture = value; }
        }

        public float Humidity
        {
            get { return _humidity; }
            set { _humidity = value; }
        }

        public float Pressure
        {
            get { return _pressure; }
            set { _pressure = value; }
        }
    }
인터페이스들
 interface ISubject
    {
        void RegisterObserver(IObserver o); //구독자 등록
        void RemoveObserver(IObserver o);   //구독 해지
        void NotifyObservers();             //새로운뭔가가 나오면 알려줌
    }

    interface IObserver
    {
        void Update(float temp, float humidity, float pressure); //업데이트 되어야할 정보들
    }

    interface IDisplayElement
    {
        void Display(); //정보가 있으니 표시를 해야겠죠?
    }
그리고 WeatherData 클래스
   class WeatherData : CInfo, ISubject
    {
        List _observers; //C#에서는 List를 이용했다.

        public WeatherData()
        {
            _observers = new List();
        }

        public void RegisterObserver(IObserver o)
        {
            _observers.Add(o);
        }

        public void RemoveObserver(IObserver o)
        {
            int i = _observers.IndexOf(o);
            if (i >= 0)
            {
                _observers.RemoveAt(i);
            }
        }

        public void NotifyObservers()
        {
            foreach (IObserver o in _observers)
            {
                o.Update(Temperture, Humidity, Pressure);
            }
        }

        public void MeasurementChanged()
        {
            NotifyObservers(); //책에서는 이런식으로 하던데
        }

        public void setMeasurement(float temperture, float humidity, float pressure)
        {
            this.Temperture = temperture;
            this.Humidity = humidity;
            this.Pressure = pressure;
            MeasurementChanged(); //그냥 바로 NotifyObservers() 함수를 바로 호출해도 제대로 작동한다,..굳이 이렇게 두번에 그쳐 호출하는 이유를 모르겠다,내공 부족
        }
    }
////<<애들은 무시
//
이렇게 되어있고

 
class Program
    {
        static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();

            CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
            //FutureConditionsDisplay futureDisplay = new FutureConditionsDisplay(weatherData);

            weatherData.setMeasurement(80, 65, 30.4f);
        }
    }

메인에서 위에서 처럼 하는데 내가 첨에 공부할때는 아래 방식으로 했다.

여기서 생성자 부분과 WeatherData 객체 선언 부분을 지우고

  
 class CurrentConditionsDisplay : CInfo, IObserver, IDisplayElement
    {
        //WeatherData weatherData; 굳이 중복되게 여기 선언할 이유가 없다.
        //public CurrentConditionsDisplay(WeatherData weatherData) 마찬가지로 생성자에서 대입해 줄 필요도 없음.
        //{
        //  this.weatherData = weatherData;
        //    weatherData.RegisterObserver(this);
        //}
          public void Update(float temperture, float humidity, float pressure)
        {
            this.Temperture = temperture;
            this.Humidity = humidity;
            Display();
        }

        public void Display()
        {
            Console.WriteLine("Current coditions: " + Temperture + "F degrees and " + Humidity + "% humidity");
        }
    }
그러면 메인에서는 아래와 같이 바꾸면됨
 
class Program
    {
        static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();

            CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();
            weatherData.RegisterObserver(currentDisplay); //이렇게 직접 옵저버 등록
            weatherData.setMeasurement(80, 65, 30.4f);
        }
    }
기능은 똑같이 작동하는데,, 처음에 왜 HeadFirst 자바에서 저렇게 구현했을까(복잡해 보였다) 책에 이런 내용이 있다. 
Q. Subject 에 대한 레프런스(WeatherData) 왜 저장하죠? 생성자 말고 다른 데서는 쓸 일이 없지 않나요? 
A. 예 올바른 지적입니다. 하지만, 나중에 옵저버 목록에서 탈퇴해야 한다면 주제 객체에 대한 레프런스를 저장해 두면 유용하게 쓸 수 있을 것입니다. 
음...유용하게라?? 언제일지 모르지만 여튼 저렇게 보관해 두는게 좋다고 한다