본문 바로가기
IT/Java

[Java] Java Observer Pattern

by kyu-nahc 2024. 9. 8.
반응형

Observer Pattern

옵저버 패턴(Observer Pattern)은 

옵저버(관찰자)들이 관찰하고 있는 대상자의 상태가 변화가 있을 때마다 

대상자는 직접 목록의 각 관찰자들에게 통지하고, 관찰자들은 알림을 받아 조치를 취하는 행동 패턴이다.


옵저버 패턴은 다른 디자인 패턴들과 다르게 일대다(one-to-many) 의존성을 가지는데,

주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행 / 구독 모델로도 알려져 있기도 하다.
이 패턴을 이해하는 데 있어 뉴스 피드나 유튜브로 비유해 보면 쉽다. 

유튜브 채널은 발행자(Subject)가 되고 구독자들은 관찰자(Observer)가 되는 구조로 보면 된다. 

실제로 유튜버가 영상을 올리면 여러 명의 구독자들은 모두 영상이 올라왔다는 알림을 받는데, 

이를 패턴 구조로 보자면 구독자들은 해당 채널을 구독함으로써 

채널에 어떠한 변화(영상을 올리거나 커뮤니티에 글을 쓰거나)가 생기게 되면 

바로 연락을 받아 탐지하는 것이다. 반면 구독을 해지하거나 안 한 시청자에게는 알림이 가지 않게 된다.

 

즉 정리하자면, 옵저버 패턴(Observer Pattern)에서는

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

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

옵저버 패턴을 구현하는 방법에는 여러 가지가 있지만,

대부분 주제(Subject) 인터페이스와 옵저버(Observer) 인터페이스가 들어있는 클래스 디자인으로 한다.

 

Observer Pattern 구조

먼저 옵저버 패턴의 구조를 보면 다음과 같다.

 

코드를 통해 살펴보면 다음과 같다.

// Subject 인터페이스
// 관찰 대상자를 정의한다.
public interface Subject {
    void removeObserver(Observer observer);
    void addObserver(Observer observer);
    void notifyObservers();
}

// Subject 인터페이스를 구현하는 구현체
// Observer를 등록,제거,알림을 발행하는 주체 클래스
public class ConcreteSubject implements Subject{

    List<Observer> observerList = new ArrayList<>();

    @Override
    public void removeObserver(Observer observer) {
        System.out.println("Remove Observer : "+observer.toString());
        observerList.remove(observer);
    }

    @Override
    public void addObserver(Observer observer) {
        System.out.println("Add Observer : "+observer.toString());
        observerList.add(observer);
    }

    @Override
    public void notifyObservers() {
        for(Observer observer : observerList){
            observer.update();
        }
    }
}
// 구독자들을 묶는 인터페이스
// 다형성을 이용
public interface Observer {
    void update();
}

// Observer 인터페이스를 구현하는 구현체
// Subject의 구현체가 발행하는 알림에 대한 행동을 취한다.
public class ObserverA implements Observer{

    @Override
    public void update(){
        System.out.println("ObserverA Update News");
    }
    public String toString(){
        return "Observer A";
    }
}

// Observer 인터페이스를 구현하는 구현체
// Subject의 구현체가 발행하는 알림에 대한 행동을 취한다.
public class ObserverB implements Observer{

    @Override
    public void update(){
        System.out.println("ObserverB Update News");
    }
    public String toString(){
        return "Observer B";
    }
}
public class Main {
    public static void main(String[] args) {

        Subject subject = new ConcreteSubject();

        // 2개의 옵저버 생성
        Observer firstObserver = new ObserverA();
        Observer secondObserver = new ObserverB();

        // 옵저버 A,B를 등록
        subject.addObserver(firstObserver);
        subject.addObserver(secondObserver);
        // 모든 옵저버에게 알림 발행
        subject.notifyObservers();

        // 옵저버 A 삭제
        subject.removeObserver(firstObserver);
        // 모든 옵저버에게 알림 발행
        subject.notifyObservers();
    }
}

 

 

Observer Pattern 특징

 

Subject

관찰 대상자를 정의하는 인터페이스


ConcreteSubject

관찰당하는 대상자 / 발행자 / 게시자

Observer들을 리스트(List, Map, Set ..등)로 가지고 있다.
Subject의 역할은 관찰자인 Observer들을 

내부 리스트에 등록 /삭제하는 인프라를 갖고 있다. (register, remove)
Subject가 상태를 변경하거나 어떤 동작을 실행할 때, Observer 들에게 이벤트 알림(notify)을 발행한다.


Observer

구독자들을 묶는 인터페이스 (다형성)


ObserverA, ObserverB

관찰자 / 구독자 / 알림 수신자.
Observer 구현체들은 Subject가 발행한 알림에 대해 현재 상태를 취득한다.
Subject의 업데이트에 대해 전후 정보를 처리한다.

 

옵저버 패턴은 여타 다른 디자인 패턴과 똑같이 상호작용할 객체를 

합성(compositoin)을 하고 메서드 위임을 통해 구성하는 코드 패턴임은 똑같지만, 

핵심은 합성한 객체를 리스트로 관리하고 리스트에 있는 관찰자 객체들에게 

모두 메서드 위임을 통한 전파 행위를 한다는 점이 주요 관점이다.

다음으로 Loose Coupling이라는 장점이 존재한다.

 

 

Loose Coupling의 위력

두 객체가 느슨하게 결합되어 있다는 것은,

그 둘이 상호작용을 하지만 서로에 대한 내부 로직으로는 관련이 없다는 것을 뜻한다.

옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 객체 디자인을 제공한다.

이 객체 디자인으로 얻는 이점은 다음과 같다.

  1. 옵저버를 언제든 새로 추가, 제거할 수 있다.
  2. 새로운 형식의 옵저버라 할지라도 주제를 전혀 변경할 필요가 없다.
  3. 주제와 옵저버는 서로 독립적으로 재사용할 수 있다.
  4. 주제나 옵저버가 바뀌더라도 서로에게 영향을 미치지 않는다.

디자인원칙을 고려하면 서로 상호작용을 하는 객체 사이에서는

가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

Loose Coupling 디자인을 활용하면 변경사항이 생겨도

무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있다. 

다음으로 옵저버 패턴의 흐름을 간단히 살펴보면 다음과 같다.


옵저버 패턴에서는 한 개의 관찰 대상자(Subject)와 여러 개의 관찰자(Observer A, B, C)로

일 대 다 관계로 구성되어 있다.

  • 관찰 대상 Subject의 상태가 바뀌면 변경사항을 옵저버한테 통보해 준다.
  • 대상자로부터 통보를 받은 Observer는
    값을 바꿀 수도 있고, 삭제하는 등 적절히 대응한다.
  • Observer들은 언제든 Subject의 그룹에서 추가 / 삭제될 수 있다. 
  • Subject 그룹에 추가되면 Subject로부터 정보를 전달받게 될 것이며, 
    그룹에서 삭제될 경우 더 이상 Subject의 정보를 받을 수 없게 된다.

 

Observer Pattern 적용 예시

많은 웹 애플리케이션을 보면 특정 웹 사이트에 가입하였을 때,

웹 사이트에 대한 정보가 메일로 많이 전송되는 것을 볼 수 있다.

해당 옵저버 패턴을 활용하여 특정 이벤트가 발생할 경우,

웹을 사용하는 사용자들에게 메일을 보내는 프로세스를 옵저버 패턴으로 구현가능하다.

 

또한 상태 변화를 계속해서 추적해야 하는 경우에 옵저버 패턴을 사용하면 유용하다.

상태가 계속 변하는 개체들을 Observer 객체로 만들고,

상태 변화를 감지하였을 때 실시할 로직들을 Override하여 구현하면 된다.

Subject를 구현하는 감시자 구현체에서는 상태 변화를 계속 추적하고,

만약 특정 상태가 변한다면 notifyObservers()를 통해

Observer 모두에게 상태 변화 로직을 실행하도록 구성하면 된다.

이에 대한 예시로는 날씨 변화 감지를 계속 Display해주는 것을 예로 들 수 있다.

 

 

 

참고자료

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%98%B5%EC%A0%80%EB%B2%84Observer-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

 

반응형

'IT > Java' 카테고리의 다른 글

[Java] Java Builder Pattern  (2) 2024.09.12
[Java] Java Factory Pattern  (1) 2024.09.11
[Java] Java Decorator Pattern  (0) 2024.09.09
[Java] Java Strategy Pattern  (0) 2024.08.29
[Java] Java 함수형 프로그래밍  (0) 2024.08.27

loading