옵저버 패턴

정의

옵저버 패턴은 객체의 상태 변화를 관찰하는 관찰자 객체들이 관찰하는 객체에 변화가 있을 때마다 변경 정보를 받고 갱신되도록 하는 디자인 패턴입니다.

옵저버 패턴은 1:1혹은 1:N 의존성을 가지는데, 주로 분산 이벤트 처리 시스템에서 많이 사용됩니다.

발행/구독 모델로 알려져 있기도 합니다.

객체 사이의 의존성을 줄이고 유연하게 상호작용하도록

즉, Coupling을 느슨하게 유지하는 것을 도와주는 패턴입니다.

이해하기 가장 쉬운 예시는 유튜브 혹은 인스타그램을 생각하면 됩니다.

유튜브에서 내가 구독한 채널이 새로운 영상을 올리면 새 영상 업로드 알림을 받습니다.

나 뿐만아니라 같은 채널을 구독한 다른 모든 구독자들도 알림을 받을것입니다.

옵저버 패턴에서 유튜브 채널이 Subject가 되고, 구독자들이 Observer가 됩니다

 

관찰자(Observer)라고 해서 관찰자 객체들이 Subject를 스스로 관찰하는 것처럼 느껴지지만
직접 관찰한다는 느낌 보다는 Subject의 변화를 통한 정보 갱신을 하기 위해 변경사항을 전달받길 기다리는 수동적인 상태에 더 가깝습니다.

 

옵저버 패턴의 클래스 다이어그램을 그리면 아래와 같습니다.

Subject와 Observer는 인터페이스 이며 이것을 구현하는 클래스가 필요합니다.

Subject에서 observerCollection을 갖는데 타입으로 L등이 될 수 있습니다.

Subject를 구현한 클래스를 ConcreteSubjet라고 하겠습니다.

ConcreteSubjets는 Observer들을 List, Set, Map 등의 Collection에 모아 갖고 있습니다.

또한 Observer들을 내부에서 등록 및 삭제하는 구조이며 Subject의 상태가 변경되면 Observer들에게 알림을 발행합니다.

 

 

옵저버 패턴 흐름

1. 한개의 주체(Subject)와 여러개의 관찰자(Observer)로 구성되어습니다.

2. Observer 패턴에서는 Subject의 상태가 바뀌면 변경사항을 Obsever들에게 전달합니다.

3. Subject로부터 받은 정보로 Observer의 정보를 바꿀 수 있습니다.

4. Observer들은 Subject의 Collection에서 삭제/추가 될 수 있습니다.

유튜브 채널이 있고, 영상을 게시했을때 구독자에게 알림을 보내는 상황을 가정하여 코드를 작성해봤습니다.

 

Observer.java

public interface Observer {
    void update(String title);
}

관찰자/구독자 역할을 하게될 Observer 인터페이스입니다.

ObserverImpl.java

public class ObserverImpl implements Observer{
    String name;
    public ObserverImpl(String name){
        this.name = name;
    }
    @Override
    public void update(String title) {
        System.out.println(this.name + "님, [" + title + "] 영상이 게시되었습니다.");
    }
}

Observer를 구현한 클래스입니다.

 

Subject.java

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObserver(String title);
}

 

SubjectImpl.java

import java.util.ArrayList;
import java.util.List;

public class SubjectImpl implements Subject{

    private final List<Observer> observerList;
    public SubjectImpl(){
        observerList = new ArrayList<>();
    }

    // Observer 추가
    @Override
    public void registerObserver(Observer o) {
        observerList.add(o);
    }

    // Observer 삭제
    @Override
    public void removeObserver(Observer o) {
        observerList.remove(o);
    }

    // Observer들에게 이벤트 알림
    @Override
    public void notifyObserver(String title) {
        for (Observer o : observerList)
            o.update(title);
    }
}

 

Subject를 구현한 클래스입니다.

Observer들을 List를 이용해 관리합니다.

옵저버를 등록하고 알림을 보내는 것을 간단하게 구현했습니다.

public class youtube {
    public static void main(String[] args) {
        // 유튜브 채널 생성
        Subject channel = new SubjectImpl();
        
        // 구독자 생성
        Observer subscriber1 = new ObserverImpl("철수");
        Observer subscriber2 = new ObserverImpl("영희");
        
        // 구독자 등록
        channel.registerObserver(subscriber1);
        channel.registerObserver(subscriber2);
        
        // 영상 업로드 알림
        channel.notifyObserver("웃긴 짤");
        channel.notifyObserver("무서운 짤");
        
        // 영희가 구독 취소
        channel.removeObserver(subscriber2);
        
        // 영상 업로드 알림
        channel.notifyObserver("슬픈 짤");
    }
}

 

 

실행 결과는 아래와 같습니다.

영희가 구독을 취소한 이후로는 영희에게 알림이 가지 않습니다.

 

 

 

장점

1. 옵저버 패턴을 사용하면 Subject의 상태 변경을 매번 일일히 확인하지 않고도 자동으로 감지가 가능합니다.

2. Subject의 코드를 변경하지 않아도 새 구독자 클래스를 도입이 가능해 OCP를 준수합니다.

3. 런타임 시점에서 Subject와 Observer가 구독 알림 관계를 맺을 수 있습니다.

4. Subject와 Observer의 관계를 느슨하게 유지할 수 있습니다.

단점

1. 알림의 순서를 제어할 수 없으며, 무작위 순서로 알림을 받게됩니다.

2. 무분별한 사용은 코드의 복잡도가 증가합니다.

3. 옵저버 객체 등록 이후 해지하지 않으면 메모리 누수가 발생할 수 있습니다.

 


MVC 패턴

정의

MVC 패턴은 사용자 인터페이스를 설계하는 데 사용되는 디자인 패턴으로, 애플리케이션을 세 가지 주요 구성 요소로 나눕니다.

1. Model: 애플리케이션의 데이터와 비즈니스 로직을 처리합니다.

2. View: 사용자 인터페이스 요소를 처리하고 모델의 데이터를 사용자에게 표시합니다.

3. Controller: 사용자 입력을 처리하고 모델과 뷰를 업데이트합니다.

옵저버 패턴은 MVC 패턴의 모델과 뷰 간의 관계를 구현하는 데 자주 사용됩니다.

모델이 옵저버 패턴의 Subject이고, 뷰가 Observer가 됩니다.

모델의 상태가 변경되면, 모델은 뷰에게 변경 사항을 알리고, 뷰는 이를 반영하여 화면을 업데이트 합니다.

이것을 통해 MVC 패턴에서 모델과 뷰 간의 느슨한 결합을 유지할 수 있습니다.


옵저버 패턴을 직접 구현하지 않아도 자바의 내장 Observer 객체가 있어 클래스를 상속하기만 하면 쉽게 이용할 수 있습니다. (java.util.Observer, java.util.Observable)

하지만 Observable는 인터페이스가 아닌 클래스입니다. Observable클래스를 상속해야 하는데, 자바에서는 단일 상속만 지원하기 때문에

Subject역할을 하는 클래스가 다른 클래스를 상속하고 있는 상태라면 내장 객체를 이용할 수 없습니다.

메서드 위임을 해주면 되지 않나 싶지만, Observable의 메서드가 protected로 선언되어 있기 때문에 결국 자식 클래스에서 호출할 수 밖에 없습니다.

따라서 내장 객체를 이용할 수 없는 상황이라면 직접 구현해줘야만 합니다.

 

 

 

 

출처 및 참고

https://shan0325.tistory.com/33

https://gsbang.tistory.com/entry/Design-Pattern-Observer-Pattern%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4

https://velog.io/@te-ing/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4

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

https://xzio.tistory.com/289

https://refactoring.guru/design-patterns/observer

https://ssdragon.tistory.com/144

 

'CS > JAVA' 카테고리의 다른 글

JVM 이해하기 (2) - Class Loader  (2) 2025.01.29
JVM 이해하기 (1) - 도입  (3) 2025.01.28

+ Recent posts