본문 바로가기
Design pattern

옵저버 패턴(observer pattern) 정리

by 평범한 개발자... 2020. 6. 17.
옵저버 패턴(observer pattern)

옵저버 패턴

  • 옵저버 패턴은 뭔가 중요한 일이 일어났을 때, 객체들한테 새 소식을 알려 줄 수 있는 패턴이다.
  • 객체는 해당 정보를 받을지 여부를 실행 중에 결정할 수 있다.
  • 이 패턴은 JDK에서 가장 많이 쓰이는 패턴 중 하나이다.

 

1. 기상 모니터링 애플리케이션 개요

  • 기상 정보 스테이션 구축 프로젝트에 참여하게 됐다.

  • 기상 스테이션으로부터 데이터를 받아서 디스플레이 장비에서 갱신해 가면서 보여 주는 애플리케이션을 구현해야 한다.

     

  • 이 시스템은 크게 2 개의 역할 군으로 구성된다.

    • 기상 스테이션

      • 습도, 온도, 압력 센서로부터 데이터를 수집
    • WeatherData 객체

      • 기상 스테이션을 통해 데이터를 취득

      • 세 개의 디스플레이 항목

        1. 현재 온도, 습도 압력
        2. 기상 통계
        3. 간단한 기상 예보
      • 추가 디스플레이를 개발할 수 있도록 확장 가능 해야함

       

  • 간단하게 코드로 구현해보자

    • WeatherData.java

     

  • 혹시 위의 코드가 어떤 점이 문제가 있는지 알겠는가?

    • 인터페이스가 아닌 구체적인 구현을 바탕으로 코딩하고 있다.

      • currentConditionsDisplay, statisticsDisplay, forecastDisplay
      • 그래도 update라는 공통된 인터페이스를 사용하고 있다.
    • 새로운 디스플레이 항목이 추가될 때마다 코드를 변경해야 한다.

      • 추가Display.update(temp, humidity, pressure) 같은 코드가 계속 추가된다.
    • 실행 중에 디스플레이 항목을 추가/제거할 수 없다.

      • 디스플레이를 추가/제거하려면 코드를 변경해야 하기 때문이다.
    • 바뀌는 부분을 캡슐화하지 않았다.

      • 디스플레이의 성격에 따라 화면에 표시되는 데이터가 다를 수 있다.
      • 현재 update 메소드의 파라미터가 모두 동일하다. (temp, humidity, pressure)

       

2. 옵저버 패턴을 본격적으로 배워보자

  • 우선 옵저버 패턴을 공부한 다음, 나중에 기상 모니터링 애플리케이션에 어떤식으로 적용 할 수 있을지 생각해보자

     

  • 발행/구독 매커니즘만 알고있다면 옵저버 패턴은 비교적 이해하기 쉬운 패턴이다. 옵저버 패턴의 정의는 아래와 같다.

  • 옵저버 패턴은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한 패턴이다.

이미지 1

  • Subject(주제) 객체의 역할

    • 변경될 데이터를 관리
    • 주제의 데이터가 변경되면 변경된 값을 옵저버들에게 전달함
  • Observer(옵저버) 객체의 역할

    • 옵저버 객체들은 주제 객체를 구독하고 있으며(주제 객체에 등록되어 있으며)

    • 주제의 데이터가 바뀌면 갱신 내용을 전달 받음

    • 위 그림의 Dog, Cat, Mouse 객체는 옵저버 인터페이스를 구현한 객체임

       

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

ㅅㅅㅅ

  • Subject(주제)

    • 주제를 나타내는 인터페이스
    • 각 주제마다 여러 개의 옵저버가 있을 수 있음 (일대다)
  • ConcreteSubject

    • Subject 인터페이스의 실제 구현 클래스
    • 옵저버의 등록 및 해지를 위한 register, remove 메소드 구현
    • 모든 옵저버들에게 연락하기 위한 notify 메소드 구현
  • Observer(옵저버)

    • 데이터의 변경을 알아야 하는 객체는 Observer 인터페이스를 구현하면 됨
    • update 메소드 하나만 존재함
  • ConcreteObserver

    • Observer 인터페이스의 실제 구현 클래스
    • update 메소드에 데이터를 처리하는 로직이 구현되면 됨
    • update 메소드외에 추가적으로 필요한 메소드를 더 구현할 수 있음

 

2.2 느슨한 결합에 대하여

  • Subject와 Observer의 의존성 관계

    • 데이터의 주인은 Subject이다.
    • 옵저버는 데이터가 변경되었을 때 갱신해주기를 기다리는 입장이기 때문에, 의존성을 가진다고 볼 수 있다.
  • 느슨한 결합

    • 옵저버 패턴은 느슨한 결합의 패턴이다.

      • Subject와 Observer는 서로 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미함
    • Subject가 Observer대해 아는 것은 그저 인터페이스를 구현했다는 것 뿐

      • Observer의 구현부(ConcreteObserver) 클래스가 무엇인지, 어떤 일을 하는지 등에 대해 알 필요가 없다.
    • Observer는 언제든 추가/제거가 가능하다.

      • 느슨한 결합이기 때문에, 코드 변경 없이 아무 때나 추가/제거가 가능하다.
    • 새로운 Observer를 추가하려고 할 때, Subject의 코드를 변경할 필요가 없다.

       

    느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있다. 객체 사이의 상호 의존성을 최소화할 수 있기 때문이다.

     

3. 다시 기상 스테이션 시스템을 구현해보자

3.1 다이어그램

83830441-1917be80-a720-11ea-9694-8fb56ed76e66

  • DisplayElement

    • 화면을 출력하는 공통 기능이 있기 때문에, 공통 인터페이스 생성
  • CurrentConditionsDisplay: 현재 기상 값 표시 화면

  • ForcastDisplay: 일기예보 화면

  • StatsticsDisplay: 기상 통계 화면

  • ThridPartDisplay: 새로 추가 될 화면

 

3.2 인터페이스 소스 코드

3.2.1 Subject

3.2.2 Observer

3.2.3 DisplayEelement

 

3.3 구현 코드

3.3.1 WeatherData

  • 기상청 API를 통해 실제 측정값을 가져 오도록 만들면 더 재미있을 것!
  • 위 코드는 setMeasurements 메소드를 통해 수동으로 값을 입력하는 테스트 코드로 구현

 

3.3.2 Display 항목

 

3.3.3 Main 소스

예제결과

 

4. Java Observer

자바 java.util 패키지에 Observer 인터페이스와 Observable(Subject) 클래스를 지원합니다.

자바에서 제공하는 옵저버 패턴은 어떤식으로 사용되고, 어떤 차이가 있는지 알아보겠습니다.

4.1 자바 내장 옵저버 패턴 작동 방식

  • 가장 큰 차이점은 주제 객체, 즉 Observable가 클래스로 구현된 구현체

    • 위 예제에서는 Subject 라는 인터페이스를 사용하여, 인터페이스에 맞게 직접 구현 하였음
    • registerObserver, removeObserver, notifyObserver 메소드를 직접 구현
    • 반면, Observable 클래스는 위 메소드가 이미 구현되어있음 (addObserver, deleteObserver, notifyObservers)
  • 자바 Observable 클래스는 위의 메소드 이외에 다른 메소드를 더 지원함

    • Observable 메소드 종류

    image

 

4.2 Observer 객체 만드는 방법

  • 위 날씨 예제에서 구현한 Observer 방법과 동일(메소드 이름만 상이)

    • java.util.Observer 인터페이스를 상속 받아 구현 후 ,Observable 객체의 addObserver 메소드 호출
    • 옵저버 목록에서 탈퇴하고 싶다면 deleteObserver 메소드 호출

4.3 Observable에서 Observer 객체에게 데이터 전달 하는 방법

  • java.util.Observable 클래스를 상속받아 주제 객체를 생성

  • setChanged() 메소드를 호출하여 객체의 상태가 바뀌었다는 것을 알림

    • 즉, 데이터가 변경되는 부분에 setChanged() 메소드를 호출
  • 그 다음 notifyObservers 메소드 호출 (2가지 종류 메소드가 존재)

    • notifyObservers()

    • notifyObservers(Object arg)

      • update 메소드의 arg 인자로 전달

4.4 Observer가 데이터를 전달 받는 방법

  • java.util.Observer에서 구현하는 update 메소드의 형태는 아래과 같음
  • 기본적으로 observable 객체를 받고, 인자가 추가적으로 필요하다면 arg 파라미터를 통해 전달 받음
  • 인자가 없다면 arg값에는 null이 전달됨

 

4.5 setChanged() 메소드?

  • 이 메소드는 상태가 바뀌었다는 것을 밝히기 위한 용도로 쓰임
  • notifyObservers() 메소드가 호출되었을 때 이 메소드에서 옵저버들을 갱신 시킴
  • setChanged() 메소드가 호출 되지 않은 상태에서 notifyObservers() 메소드가 호출되면 옵저버에게 데이터를 갱신시키지 않음
  • 아래는 코드는 Observable 클래스의 일부

 

4.6 자바 내장 옵저버를 이용한 가상 기상스테이션 구현 코드

4.6.1 WeatherData 클래스

  • 옵저버를 관리하는 코드와 메소드는 Observable 클래스에 모두 있기 때문에, 따로 구현하지 않아도 됨
  • WeatherData 클래스에서는 언제, 어떤 데이터를 줄 것 인가에 대한 코드만 구현

 

4.6.2 Display 클래스

  • java.uill.Observer 인터페이스 상속
  • java.util.Observable 클래스 래퍼런스 인자 생성

 

4.7 자바 내장 옵저버 단점

  • Observable은 인터페이스가 아니라 클래스라는 점

    • 다른 수퍼 클래스를 상속받아 확장하고 있는경우 Observable의 기능을 추가 할 수 없음, 따라서 재사용성이 제약되어 디자인패턴의 사용 목적을 상실
  • Observable의 핵심 메소드를 외부에서 호출 할 수 없음

    • setChagned() 메소드가 protected로 선언되어있어, 서브클래스가 아닌 인트턴스 변수로 사용 불가능
  • 결론

    • java.util.Observable을 상속하여 사용할 수 있는 상황이라면 Observable API를 쓰는 것도 괜찮지만, 직접 옵저버 패턴을 구현해야 할 수도 있다.
    • 핵심은 옵저버 패턴만 제대로 알고 있다면 그 패턴을 활용하는 API는 어떤 것이든 잘 활용할 수 있을 것
반응형

댓글