02. 옵저버 패턴 - 객체들에게 연락 돌리기

솔트커피·2022년 7월 11일
0
  • 신문사가 사업을 시작하고 신문을 찍어내기 시작합니다.
  • 독자가 특정 신문사에 구독 신청을 하면 매번 신문이 나올때마다 배달을 받을 수 있습니다.
    구독을 해지하기 전까지 신문을 계속 받을 수 있습니다.
  • 신문을 더 이상 보고 싶지 않으면 구독 해지 신청을 합니다.
    그러면 더이상 신문이 오지 않습니다.
  • 신문사가 망하지 않는 이상 개인, 호텔, 항공사 및 기타 회사등은 꾸준하게 신문을 구독하거나 해지합니다. 

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

신문 구독 메커니즘만 제대로 이해할 수 있다면 옵저버 패턴을 쉽게 이해할 수 있습니다.

신부사를 주제(subject), 구독자를 옵저버(observer)라고 부른다는 것만 외워 두세요.

옵저버 패턴의 정의 (p87)

옵저버 패턴(Observer Pattern)은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에 연락이 가고

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

옵저버 패턴은 여러 가지 방법으로 구현할 수 있지만, 보통은 주제 인터페이스와 옵저버 인터페이스가 들어있는 클래스 디자인으로 구현합니다.

출판-구독(Pub-Sub)패턴은 옵저버 패턴의 다른 이름인가요? (p88)

아뇨, 관계가 있긴 하지만 같진 않아요. 출판-구독 패턴은 구독자가 서로 다른 유형의 메시지에 관심을 가질 수 있고,

출판사와 구독자를 더 세세하게 분리할 수 있는 복잡한 패턴입니다. 미들웨어 시스템에서 종종 쓰입니다.

느슨한 결합의 위력(p90)

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

옵저버 패턴은 느슨한 결합을 보여주는 훌륭한 예입니다. 옵저버 패턴에서 어떤 식으로 느슨한 결합을 만드는지 알아볼까요?

  • 주제는 옵저버가 특정 인터페이스(Observer 인터페이스)를 구현한다는 사실만 압니다.
    • 옵저버의 구상 클래스가 무엇인지, 옵저버가 무엇을 하는지는 알 필요도 없습니다.
  • 옵저버는 언제든지 새로 추가할 수 있습니다.
    • 주제는 Observer 인터페이스를 구현하는 객체의 목록에만 의존하므로 언제든지 옵저버를 추가하거나, 변경하거나, 제거할 수 있습니다.
  • 새로운 형식의 옵저버를 추가할 때도 주제를 변경할 필요가 전혀 없습니다.
    • 옵저버가 되어야하는 새로운 구상 클래스가 생겨도, 이 클래스를 받아들일 수 있도록 주제를 바꿔야 할 필요는 없습니다.
      Observer 인터페이스만 구현한다면 어떤 객체에도 연락할 수 있으니까요.
  • 주제와 옵저버는 서로 독립적으로 재사용할 수 있습니다.
  • 주제나 옵저버가 달라져도 서로에게 영향을 미치지는 않습니다.

디자인 원칙 - 상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다.

Q1. 자바에는 Observable 클래스도 있지 않았나요? (104p)

아, 좋은 질문입니다. 자바에는 옵저버 패턴용 Observable 클래스와 Observer 인터페이스가 있었죠.

Observable 클래스는 수이가 직접 코드를 작성하지 않아도 옵저버를 추가하고 삭제하고 옵저버에 알림을 보내는 메소드를 제공했습니다.

Observer 인터페이스는 update() 메소드를 포함하여 앞에서 우리가 만든것과 유사한 인터페이스를 제공했죠.

이 클래스들은 자바 9 이후로는 쓰이지 않습니다.

각자 자신의 코드에서 기본적인 옵저버 패턴을 지원하는 게 더 편하다고 생각하는 사람들과

더 강력한 기능을 스스로 구현하는게 낫겠다고 생각하는 사람들이 늘어나면서 Observer와 Observable은 역사의 뒤안길로 사라지고 말았습니다.

Q2. 자바에서 그런 클래스들을 대신할 만한 기능을 새로 탑재해 주진 않았나요?

자바빈은 Bean에서 PropertyChangeEvents로 유형 속성이 바뀌었을 때 PropertyChangeListener에 알림을 보내 주는 기능을 제공합니다.

또한 비동기 스트림을 처리하는 Flow API와 관련된 출판-구독 구성 요소도 제공합니다.

Q3. 주제에서 옵저버로 알림이 가는 순서를 정해야 하나요?

Observer를 알림 순서에 의존하지 말라는 JDK 권고가 있습니다.

푸시를 풀로 바꾸는건 정말 좋은 생각입니다.
아까 방구석 토크에서 주제와 옵저버가 푸시(주제가 옵저버에게 상태를 알리는 방식)와 풀(옵저버가 주제로부터 상태를 끌어오는 방식을) 가지고 토론했던 걸 다시 생각해 봤어요.

디스플레이가 WeatherData 객체로부터 필요할 때마다 데이터를 끌어오면 코드를 조금 더 일반화할 수 있지 않나요? 그러면 나중에 새로운 디스플레이를 추가하기도 더 쉬울 것 같은데요?

지금 만들어놓은 WeatherData 디자인은 하나의 데이터만 갱신해도 되는 상황에서도 update() 메소드에 모든 데이터를 보내도록 되어 있습니다.

그래도 별문제는 없지만, 나중에 Weather-O-Rama에서 풍속 같은 새로운 데이터 값을 추가한다면 어떨까요?

대부분의 update() 메소드에서 풍속 데이터를 쓰지 않더라도 모든 디스플레이에 있는 update() 메소드를 바꿔야 하지 않을까요?

사실 푸시를 사용하거나 풀을 사용하는 방법중 어느 하나를 선택하는 일은 구현 방법의 문제라고 볼 수 있습니다.

하지만 대체로 옵저버가 필요한 데이터를 골라서 가져가도록 만드는 방법이 더 좋습니다.

시간이 지남에 따라 애플리케이션은 계속 바뀌고 점점 더 복잡해집니다.

실제로 CEO인 조니 허리케인은 기상 스테이션을 확장해서 더 많은 디스플레이를 팔고 싶어 합니다.

그러니 나중에 더 쉽게 확장할 수 있도록 디자인을 다시 한번 살펴봐야 합니다. 

옵저버가 필요한 데이터를 당겨올 수 있도록 기상 스테이션 코드를 수정하는 일은 그리 어렵지 않습니다.

주제가 자신의 데이터에 관한 getter 메소드를 가지게 만들고 필요한 데이터를 당겨올 때 해당 메소드를 호출할 수 있도록 옵저버를 고쳐 주기만 하면 됩니다.

풀 방식으로 코드 바꾸기 (105p)

지금은 주제 데이터가 바뀌면 update()를 호출해서 옵저버에 새로운 온도, 습도, 기압 데이터를 보냅니다.

값이 변했다는 알림을 옵저버가 받았을 때 주제에 있는 게터 메소드를 호출해서 필요한 값을 당겨오도록 만들어 봅시다.

풀 방식으로 바꾸려면 기존 코드를 살짝 고쳐야 합니다.

  • 주제에서 알림 보내기
    • 옵저버의 update 메소드를 인자 없이 호출하도록 WeatherData의 notifyObservers() 메소드를 수정합니다.
  • 옵저버에서 알림 받기
    • Observer 인터페이스에서 update() 메소드에 매개변수가 없도록 서명을 바꿔 줍니다.
    • 마지막으로 update() 메소드의 서명을 바꾸고 WeatherData의 게터 메소드로
      주제의 날씨 데이터를 가져오도록 각 Observer 구상 클래스를 수정합니다.

핵심 정리 (108p)

  • 객체지향 원칙
    • 바뀌는 부분은 캡슐화한다.
    • 상속보다는 구성을 활용한다.
    • 구현보다는 인터페이스에 맞춰서 프로그래밍 한다.
    • 상호작용하는 객체 사이에서는 가능하면 느슨한 결합을 사용해야 한다.
      • 느슨한 결합을 이용하는 디자인이 훨씬 더 유연하고 변화에 강하다는 사실을 꼭 기억해두세요
  • 객체지향 패턴 - 옵저버 패턴
    • 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게
    • 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의합니다.
  • 옵저버 패턴은 객체들 사이에 일대다 관계를 정의합니다.
  • 주제(Subject)는 동일한 인터페이스를 써서 옵저버에게 연락을 합니다.
  • Observer 인터페이스를 구현하기만 하면 어떤 구상 클래스와 옵저버라도 패턴에 참여할 수 있습니다.
  • 주제는 옵저버들이 Observer 인터페이스를 구현한다는 것을 제외하면 옵저버에 관해 전혀 모릅니다.
    따라서 이들 사이의 결합은 느슨한 결합니다.
  • 옵저버 패턴을 사용하면 주제가 데이터를 보내거나(푸시 방식)
    옵저버가 데이터를 가져올(풀 방식) 수 있습니다. (익반적으로 풀 방식이 더 '옳은' 방식이라고 간주합니다).
  • 스윙과 같은 GUI 프레임워크나 RxJava, 자바 Bean, RMI외에 코코아나 스위프트, 자바스크립트와 같은 다른 언어의 프레임워크에서도 옵저버 패턴을 많이 사용합니다.
  • 옵저버 패턴은 여러 개의 주제와 메시지 유형이 있는 복잡한 상황에서 사용하는 출판-구독 패턴과 친척입니다.
  • 옵저버 패턴은 자주 쓰이는 패턴으로 MVC를 배울 때 다시 만날수 있습니다.

0개의 댓글