글 검색

공개된 글의 제목과 본문을 검색합니다.

    닫기 Esc

    옵저버 패턴이란?#

    옵저버패턴 클래스 다이어그램
    옵저버패턴 클래스 다이어그램

    신문사 + 구독자 = 옵저버 패턴

    • 신문사는 주제(subject), 구독자를 옵저버(observer)라고 부른다.
    • 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에 연락이 가고 자동으로 내용이 갱신되는 방식으로 다대다 의존성을 정의합니다.
    • 옵저버 패턴은 보통 주제 인터페이스와 옵저버 인터페이스가 들어있는 클래스 디자인으로 구현합니다.
    • 옵저버로 등록하거나 탈퇴하고 싶은 경우 Subject인터페이스에 있는 메소드를 사용합니다.
    • 옵저버가 될 가능성이 있는 객체는 Observer인터페이스를 구현해야합니다.
      • 주제의 상태가 바뀌었을 때 호출되는 update() 메소드만 구현
    • 옵저버에는 데이터를 보내주는 푸시(push) 방식과 옵저버가 데이터를 가져오는 풀(pull) 방식으로 구분할 수 있습니다.
      • Push 방식의 경우 매게변수가 변경될 가능성이 있다.
      • Pull 방식의 경우 옵저버가 필요한 데이터를 가져올 수있기 때문에 더 느슨하게 결합될 수 있다.
      • 대체로 옵저버가 필요한 데이터를 가져오도록 하는게 더 좋은 방법입니다.

    느슨한 결합#

    • 느슨한 결합은 객체들이 상호작용 할 수는 있지만, 서로를 잘 모르는 관계를 의미합니다.

      → 느슨할 결합을 이용하면 유연성이 아주 좋아짐

    • 주제는 옵저버가 특정 인터페이스를 구현한다는 사실만 알고있습니다.
    • 새로운 옵저버가 추가될때도 주제는 신경쓸 필요없이 Observer인터페이스만 구현되어 있으면 됩니다.
    • 주제나 옵저버를 다은 용도로 활용할 일이 있다고 해도 손쉽게 재활용이 가능합니다.
    • 서로 느슨하게 결합되어 있으므로 주제나 옵저버 인터페이스를 구현한다는 조건만 만족한다면 어떻게 고쳐도 문제가 생기지 않습니다.

    요구사항#

    Weather-O-Rama의 차세대 인터넷 기반 기상 스테이션 구축 시스템

    기상정보 객체 다이어그램
    기상정보 객체 다이어그램
    호출되는 객체의 메소드
    호출되는 객체의 메소드
    • Weather-O-Rama와 계약을 체결하면 WeatherData 객체를 통해 현재 조건, 기상통계, 기상 예보 이 3가지 디스플레이에 갱신하며 보여주어야 함
    • 기상 관측이 변경된다면 measurementsChanged() 가 호출된다.
    • 다른 개발자가 새로운 디스플레이를 추가할 수 있고, 이후 화면에 몇가지의 항목들이 더 추가 될 수 있음

    해결 방법 1 : 메소드 직접 호출#

    첫번째 해결 방법은 아주 단순하게 measurementsChanged() 메소드에 디스플레이 객체의 메소드를 직접호출하는 방법이다.

    java
    public class WeatherData {
        public void  measurementsChanged() {
            float temp = getTemperature();
            float humidity = getHumidity();
            float pressure = getPressure();
    
            currentConditionsDisplay.update(temp, humidity, pressure);
            statisticDisplay.update(temp, humidity, pressure);
            forecastDisplay.update(temp, humidity, pressure);
        }
    }

    단점#

    • 디스플레이 객체가 변경될 수도 있다.
      • 바뀔 수 있는 부분은 캡슐화 해야한다.
    • 구체적인 구현에 맞춰 코딩했으므로 프로그램을 고치지 않고는 다른 디스플레이 항목을 추가할 수 없다.
    • 실행 중 디스플레이 객체를 더하거나 뺄수없다.

    해결 방법 2 : 옵저버 패턴 적용#

    이제 옵저버 패턴을 적용해 느슨한 결합을 만족시키고, 확장성을 지니도록 변경해보겠습니다.

    java
    public interface Subject {
        void registerObserver(Observer o);
        void removeObserver(Observer o);
        void notifyObservers();
    }
    주제가 되는 인터페이스
    java
    public interface Observer {
    		// push 방식
        void update(float temperature, float humidity, float pressure);
    		// pull 방식
        void update();
    }
    옵저버 인터페이스
    java
    public interface DisplayElement {
    		void display();
    }
    디스플레이 인터페이스
    java
    public class WeatherData implements Subject {
        private List<Observer> observers;
        private float temperature;
        private float humidity;
        private float pressure;
        
        public WeatherData() {
            this.observers = new ArrayList<Observer>();
        }
        @Override
        public void registerObserver(Observer o) {
            observers.add(o);
        }
        @Override
        public void removeObserver(Observer o) {
            observers.remove(observers.indexOf(o));
        }
        @Override
        public void notifyObservers() {
    				// 실질적인 옵저버의 호출을 담당
            for (Observer observer : observers) {
                observer.update(temperature, humidity, pressure);
            }
        }
    
        float getTemperature() {
            return temperature;
        }
        float getHumidity() {
            return humidity;
        }
        float getPressure() {
            return pressure;
        }
    
        void measurementsChanged(){
            notifyObservers();
        }
    
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
        ...
    }
    주제를 구현한 WeatherData 클래스
    java
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
        private float temperature;
        private float humidity;
    		private WeatherData weatherData;
    
        public CurrentConditionsDisplay(WeatherData weatherData) {
            this.weatherData = weatherData;
            weatherData.registerObserver(this);
        }
    
        // 푸시 방식을 경우
        @Override
        public void update(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            display();
        }
    
        // 풀 방식을 경우
        @Override
        public void update() {
            this.temperature = weatherData.getTemperature();
            this.humidity = WeatherData.getHumidity();
            display();
        }
    
        @Override
        public void display() {
            System.out.println("현재 상태: 온도 " + temperature
                    + "F, 습도 " + humidity + "%");
        }
    }
    옵저버 디스플레이

    장점#

    • 객체들간 느슨한 결합을 이뤄내었기 때문에 각자의 책임에만 집중하면 된다.
    • 런타임 도중에도 옵저버 객체를 추가하거나, 삭제할 수 있다.
    • 옵저버 객체도 주제 인터페이스를 구현한다면 주제이자 옵저버가 될 수 있다.

    정리#

    • 옵저버 패턴은 객체들 사이에 일대다 관계를 정의합니다.
    • 옵저버 패턴을 사용하면 주제가 데이터를 보내거나 옵저버가 데이터를 가져올 수 있지만 일반적으로 데이터를 가져오는게 더 옳은 방식입니다.
    • 옵저버 패턴은 자주 사용되는 패턴으로 MVC를 배울때도 다시 거론됩니다.
    전체 글 둘러보기