이카's
article thumbnail

옵저버 패턴(Observer)

옵저버 패턴은 객체의 상태를 관찰하고 상태변화가 있을 경우 알림을 받는 것을 말한다. 판매자가 하나 있다면, 이를 여러명의 옵저버가 보고 있으며 판매자가 어떤 행동을 하여 상태의 변화가 있다면 이를 옵저버가 알림을 해주는 것이다.

예로 들자면 집을 구하고자 하는 사람이 공인중개사에 매번 집 매물을 보러 가는게 아닌, 공인중개사에서 매물이 나오는 걸 체크하고 있다가 매물이 나오면 구매자한테 알려주는 방법이 옵저버 패턴이다.

 

 

옵저버 패턴은?

행동 패턴으로 어떤 이벤트(행동)이 발생했을 때, 이를 감지하고 결과를 준다. 옵저버 패턴은 SOLID 원칙에 개방-폐쇄 원칙(Open Close Principle)을 따르고 있다.

새로운 옵저버가 작성되더라도 판매자는 바뀌는게 없기 때문이다. 판매자가 발생시키는 이벤트는 개별의 옵저버가 내부적으로 구현한다. 즉, 판매자는 옵저버가 어떤 구현기능을 가지는지, 추가되는지 알 필요가 없다.

 

문제점 or 단점

만약 옵저버가 없다고 가정을 해보자.
구매자는 매번 어떤 시간에 주기적으로 제품이 있나 확인하러 판매자에게 갈 것이다. 하지만 이는 너무 비효율 적이라는 것을 직감적으로 알 수 있다.

이를 해결하기 위해 옵저버 패턴 기능을 구상하게 되었다.

단점으로 두 가지가 있다.

  1. 옵저버의 응답 순서를 보장하지 않는다.
  2. 옵저버를 명시적으로 추가, 제거하기 때문에 메모리 누수가 발생할 가능성이 존재한다.

 

옵저버의 순서를 반드시 보장하지 않는다.

정보를 전달하는 것에 의미를 두고 있기 대문에 순서를 보장하지 않는다.

 

메모리 누수

옵저버를 명시적으로 추가, 제거하기 한다. 이는 따로 핸들링 하지 않으면 메모리가 잡혀 있다는 뜻이다. 즉, 옵저버를 등록을 해두고 제거하지 않는 상태에서 옵저버를 사용하지 않는다면 판매자에서는 옵저버를 참고하고 있다. 따라서 GC 대상이 되지 않아 메모리 누수가 발생할 수 있다.

 

장점 or 해결 or 결과

대표적으로 비용 감소가 큰 장점이다. 리소스를 줄일 수 있다는 점이다.
옵저버를 사용하므로써 구매자는 효율적으로 제품이 나왔는지 알 수 있게 된다. 즉, 주기적으로 확인하지 않아도 되므로 이에 따른 비용이 감소하게 된다.

대표적인 예시로 인스타그램 팔로우 기능이다. 팔로우를 하게 되면 팔로워는 팔로우한 사람이 쓴 글 알람을 바로 받을 수 있다.

 

구현

// 제품
interface Subject {
    public void register(Observer obj);    // 제품 등록
    public void unregister(Observer obj);  // 제품 삭제
    public void notifyObservers();         // 정보 전달
    public Object getUpdate(Observer obj); 
}
// 옵저버 패턴
interface Observer {
    public void update(); // 이벤트 발생시 알려줄 메서드
}
// 판매자
class Topic implements Subject {
    private List<Observer> observers;
    private String message;

    public Topic() {
        this.observers = new ArrayList<>();
        this.message = "";
    }

    @Override
    public void register(Observer obj) {
        if (!observers.contains(obj)) observers.add(obj); 
    }

    @Override
    public void unregister(Observer obj) {
        observers.remove(obj); 
    }

    @Override
    public void notifyObservers() {
        this.observers.forEach(Observer::update); 
    }

    @Override
    public Object getUpdate(Observer obj) {
        return this.message;
    } 

    public void postMessage(String msg) {
        System.out.println("Message sended to Topic: " + msg);
        this.message = msg; 
        notifyObservers();
    }
}
// 구매자
class TopicSubscriber implements Observer {
    private String name;
    private Subject topic;

    public TopicSubscriber(String name, Subject topic) {
        this.name = name;
        this.topic = topic;
    }

    @Override
    public void update() {
        String msg = (String) topic.getUpdate(this); 
        System.out.println(name + ":: got message >> " + msg); 
    } 
}
@TEST
public class HelloWorld { 
    public static void main(String[] args) {
        Topic topic = new Topic(); 
        Observer a = new TopicSubscriber("a", topic);
        Observer b = new TopicSubscriber("b", topic);
        Observer c = new TopicSubscriber("c", topic);
        topic.register(a);
        topic.register(b);
        topic.register(c);

        topic.postMessage("amumu is op champion!!"); 
    }
}
/*
Message sended to Topic: amumu is op champion!!
a:: got message >> amumu is op champion!!
b:: got message >> amumu is op champion!!
c:: got message >> amumu is op champion!!
*/

 

자바에서 제공하는 옵저버 패턴 구현

  1. java.util.Observer / Obsevable
import java.util.Observer;
class TopicSubscriber implements Observer {
}
import java.util.Observable;
class Topic extends Observable {
  this.setChanged(); // 한번에 변화에 한번의 전달만 이루어 지게 만듬
  /* synchronized (this) {
      if (!changed) return;
      arrLocal = obs.toString();
      clearChanged();
  } */
 }
  1. java.bean.PropertyChangeListener / PropertyChangeSupport
  2. java.util.concurrent.Flow
    • Publisher / Subscriber 패턴
      중간에 Event Channel이 상호작용 한다.

 

Referance

참고 자료
CS전공지식노트

반응형
profile

이카's

@Edan Cafe ☕

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!