Notice
Recent Posts
Recent Comments
Link
«   2025/12   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

기록하자..

관찰자 패턴(Observer Pattern) 본문

디자인패턴공부

관찰자 패턴(Observer Pattern)

P23Yong 2021. 10. 19. 21:03

ObserverPattern

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 자동으로 연락이 가고

자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의

 

객체가 관심 있어하는 사건의 발생을 알려주어야 할 때 사용한다.

관찰하는 객체가 능동적으로 관찰하는 것이 아니라 관찰 대상으로부터 어떤 사건이 발생했을 때 수동적으로 알려주기를 기다린다.

관찰자가 관찰대상에게서 정보를 가져오는 방법은 2가지가 있다. 

  • Polling : 관찰자가 주기적으로 관찰대상을 관찰해 변화가 있으면 가져온다.
  • 관찰자를 관찰대상에 등록 : 정보가 갱신될 때마다 관찰자에게 통보해준다.

observer pattern은 두 번째 방식이라고 할 수 있다.


/** Head First Design에서 문제를 가져왔다. **/

문제의 시작

한 회사에서 기상 모니터링 애플리케이션(Weather Monitoring App)을 만든다고 한다.

기상 데이터가 변할 때마다 디스플레이에 표시해주고 싶다고 한다.

 

처음 주어진 소스 파일이 도착했다.

getPressure(), getHumidity(), getTemperature() 모두 최근에 측정된 기압, 습도, 온도 값을 리턴하는 메서드이다.

measurementsChanged() 메서드는 새로운 측정 결과가 있을 때마다 자동으로 호출된다. 이 메서드는 정보를 기다리는 측에게 정보를 전달한다.

 

첫 번째 시도

measurementsChanged()메서드를 작성해보자

public void measurementsChanged() {
    float temperature = getTemperature();
    float humidity = getHumidity();
    float pressure = getPressure();
    
    currentConditionDisplay.update(temperature, humidity, pressure);
    statisticsDisplay.update(temperature, humidity, pressure);
    forecastDisplay.update(temperature, humidity, pressure);
}

위의 구현은 어떤 특징이 있을까?

  • 인터페이스가 아닌 구체적인 구현을 바탕으로 코딩
  • 새로운 디스플레이 항목이 추가될 때마다 코드를 변경
  • 실행 중에 디스플레이 항목을 추가/제거할 수 없다.
  • 바뀌는 부분을 캡슐화하지 않았다.

위와 같이 많은 문제점이 있다. 그렇다면 Observer Pattern을 적용해보자.

 

두 번째 시도

시도하기에 앞서 Observer Pattern은 다음과 같은 시나리오에서 적용할 수 있다.

신문사(Publisher)와 구독자(Subscriber)들을 생각해보자.

 

  • 신문사는 신문사를 발행한다.
  • 고객은 신문이 새롭게 발행될 때마다 신문을 받아보기 위해선 구독을 해야 한다.
  • 언제든지 구독을 중단할 수 있고, 중단하면 신문이 배달되지 않는다.
  • 누구나 신문을 구독할 수 있다.

Observer Pattern = Publisher + Subscriber라고 볼 수 있다.

 

Observer Pattern의 구조에 대해 보자.

Observer는 여러 개의 subject에 관심을 가질 수 있고

Observer는 자신이 등록한 subject를 멤버로 유지할 수 있다.


Subject와 Observer 간 관계

  • Subject가 oberserver에 대해 알고 있는 유일한 정보는 Observer인터페이스를 구현하고 있다는 것이다.
  • 새 observer를 쉽게 추가할 수 있고 기존 observer를 쉽게 제거할 수 있다.
  • 새로운 형식의 observer를 추가하려 해도 subject를 변경할 필요가 없다.
  • observer나 subject의 수정은 서로에게 영향을 주지 않는다. (단, interface는 충실히 구현할 경우)

 

그럼 이제 Weather Monitoring App을 코드로 구현해보자.

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}
public interface Observer {
    public void update(float temperature, float humidity, float pressure);
}

이렇게 Observer interface의 update메서드를 구현하면 후에 문제가 없을까? 나중에 살펴보도록 하자.

public interface Display() {
    void display();
}
public class WeatherData implements Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private float temperature;
    private float humidity;
    private float pressure;
    
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    public void notifyObservers() {
        observers.forEach(o -> o.update(temperature, humidity, pressure));
    }
    
    public void measurementsChanged() {
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private float pressure;
    
    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
    
    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " +
            humidity + "% humidity");
    }
}

위에서 언급했던 update() 메서드에 대해 말해보자.

Push VS Pull

Push방식은 subject가 통보할 때 observer에게 정보까지 제공하는 것이고,

pull은 subject가 observer에게 변경 사실이 있다는 것만 통보하고 정보는 observer가 별도로 요구하여 받는다.

 

observer측면에서 push방식이 더 편리하다. → 필요한 데이터를 다 받고 subject에 의존하지 않기 때문.. (느슨한 연결)

  - pull을 해야 하는 경우 subject에 대한 더 많은 정보가 필요하고 호출해야 하는 메서드도 많을 수 있다.

  - 하지만 observer에 따라 필요로 하는 정보가 다를 수 있어 불필요한 정보를 전달하게 될 가능성이 있다.

  - 극단적인 경우를 생각하면 다중 스레드 환경에서 pull방식을 이용하면 변경 소식을 받고 얻어오는 사이 또 정보가

    바뀌는 경우를 생각하면 상태 변화가 발생할 수 있다.

subject 측면에서는 pull 방식이, observer 측면에서는 push방식이 더 편리하다.

 

push방식을 사용한 경우의 update 메서드

@Override
public void update(float temperature, float humidity, floatpressure) {
    this.temperature = temperature;
}

pull방식을 사용한 경우의 update메서드

@Override
public void update(WeatherData weatherData) {
    temperature = weatherData.getTemperature();
}

Observer Pattern 정리

장점

  • Subject에 대한 수정 없이 observer를 추가할 수 있으며 동적으로 추가/제거가 가능하다.
  • Subject는 observer가 update메서드를 가지고 있다는 것 외에는 알 필요가 없다

주의사항

  • 연쇄적 통보 발생
  •   - 경우 1. Subject가 observer에게 통보하고 observer가 subject가 되어 다른 observer에게 통보
  •   - 경우 2. Subject가 observer에게 통보하고 observer가 subject의 상태를 변경해 통보가 반복되는 상태

push와 pull

  • push 방식은 관찰대상이 관찰자에게 상태 변화를 알릴 때 변한 정보까지 제공
  • pull 방식은 관찰자에게 상태 변화 사실만 알림
  • pull에서 관찰자는 관찰대상을 update 메서드의 인자로 받아 필요한 정보를 얻기 위한 메서드를 호출해야 한다.
  •   - 이 때문에 관찰대상과 긴밀한 연결이 될 수 있다는 문제점 발생.
  • 필요한 데이터가 바뀔 경우에는 pull방식은 메서드를 수정 없이 이를 구현할 수 있다.
  • push는 메서드의 인자로 데이터가 전달되기 때문에 관찰대상과 느슨한 연결.  하지만 관찰대상에 관심이 있는 관찰자가 다양할 경우 불필요한 정보까지 수신할 수 있다는 문제점 발생

'디자인패턴공부' 카테고리의 다른 글

명령 패턴(Command Pattern)  (0) 2021.11.28
생성 패턴(Factory Pattern)  (0) 2021.11.28
Decorator 패턴  (0) 2021.10.26
전략 패턴(Strategy Pattern) : Head First Design Pattern  (0) 2021.10.19
SOLID  (2) 2021.10.15