Skip to content

Latest commit

 

History

History
191 lines (141 loc) · 6.33 KB

옵저버 패턴.md

File metadata and controls

191 lines (141 loc) · 6.33 KB

👀 옵저버 패턴 (observer pattern)

상태를 가지고 있는 주체 객체 & 상태의 변경을 알아야 하는 관찰 객체

  • 서로의 정보를 주고받는 과정에서 정보의 단위가 클수록, 객체들의 규모가 클수록 복잡성이 증가하게 됨

  • 이때 가이드라인을 제시해줄 수 있는 것이 '옵저버 패턴'

  • 주체가 어떤 객체(subject)의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴

  • 주체: 객체의 상태 변화를 보고 있는 관찰자
  • 옵저버들: 이 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 '추가 변화사항'이 생기는 객체들을 의미함
  • 주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축하기도 함

🏘️ 예시

  • 옵저버 패턴을 활용한 서비스 예시: 트위터
    • 내가 어떤 사람인 주체를 '팔로우'했다면 주체가 포스팅을 울리게 되면 알림이 '팔로워'에게 가야함
  • 옵저버 패턴은 주로 이벤트 기반 시스템에 사용하며 MVC(Model-View-Controller) 패턴에도 사용됨
    • 예를 들어 주체라고 볼 수 있는 모델(model)에서 변경 사항이 생겨 update() 메서드로 옵저버인 뷰에 알려주고 이를 기반으로 컨트롤러(controller) 등이 작동하는 것

✏️ 자바에서의 옵저버 패턴

Publisher 인터페이스

Observer들을 관리하는 메소드를 가지고 있음

  • 옵저버 등록(add), 제외(delete), 옵저버들에게 정보를 알려줌(notifyObserver)
public interface Publisher {
public void add(Observer observer);
public void delete(Observer observer);
public void notifyObserver();
}

Observer 인터페이스

정보를 업데이트(update)

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

NewsMachine 클래스

Publisher를 구현한 클래스로, 정보를 제공해주는 퍼블리셔가 됨

public class NewsMachine implements Publisher {
private ArrayList<Observer> observers;
private String title;
private String news;

    public NewsMachine() {
        observers = new ArrayList<>();
    }

    @Override
    public void add(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void delete(Observer observer) 	{
        int index = observers.indexOf(observer);
        observers.remove(index);
    }

    @Override
    public void notifyObserver() {
        for(Observer observer : observers) {
           observer.update(title, news);
        }
    }

    public void setNewsInfo(String title, String news) {
        this.title = title;
        this.news = news;
        notifyObserver();
    }

    public String getTitle() { return title; }
    public String getNews() { return news; }

}

AnnualSubscriber, EventSubscriber 클래스

Observer를 구현한 클래스들로, notifyObserver()를 호출하면서 알려줄 때마다 Update가 호출됨

public class EventSubscriber implements Observer {

    private String newsString;
    private Publisher publisher;

    public EventSubscriber(Publisher publisher) {
        this.publisher = publisher;
        publisher.add(this);
    }

    @Override
    public void update(String title, String news) {
        newsString = title + " " + news;
        display();
    }

    public void withdraw() {
        publisher.delete(this);
    }

    public void display() {
        System.out.println("이벤트 유저");
        System.out.println(newsString);
    }
}

자바: 상속과 구현

  1. 상속

    • 상속(extends)은 자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있는 것
    • 이로 인해 재사용성, 중복성의 최소화가 이루어짐
  2. 구현

    • 부모 인터페이스(interface)를 자식 클래스에서 재정의하여 구현하는 것을 말함
    • 상속과는 달리 반드시 부모 클래스의 메서드를 재정의하여 구현해야 함
  3. 상속과 구현의 차이

    • 상속은 일반 클래스, abstract 클래스를 기반으로 구현함
    • 구현은 인터페이스를 기반으로 구현함

✏️ 자바스크립트에서의 옵저버 패턴

  • 프록시 객체를 통해 구현할 수도 있음

프록시 객체

  • 프록시(proxy) 객체는 어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체를 뜻하며, 자바스크립트에서 프록시 객체는 두 개의 매개변수를 가짐

  • target: 프록시할 대상

  • handler: target동작을 가로채고 어떠한 동작을 할 것인지가 설정되어 있는 함수

  • 프록시 객체를 구현한 코드

const handler = {
  get: function (target, name) {
    return name === "name" ? `${target.a} ${target.b}` : target[name];
  },
};
const p = new Proxy({ a: "KUNDOL", b: "IS AUMUMU ZANGIN" }, handler);
console.log(p.name); // KUNDOL IS AUMUMU ZANGIN
  • new Proxy()로 a와 b 속성을 가지고 있는 객체와 handler 함수를 매개변수로 넣고 p라는 변수를 선언함
  • 이후 p의 name 속성을 참조하니 a와 b라는 속성밖에 없는 객체가 handler의 "name"이라는 속성에 접근할 때 a와 b를 합쳐서 문자열을 만들라"는 로직에 따라 어떤 문자열을 만듬
  • 이렇게 name 속성 등 특정 속성에 접근할 때 그 부분을 가로채서 어떠한 로직을 강제할 수 있는 것이 프록시 객체임

프록시 객체를 이용한 옵저버 패턴

function createReactiveObject(target, callback) {
  const proxy = new Proxy(target, {
    set(obj, prop, value) {
      if (value !== obj[prop]) {
        const prev = obj[prop];
        obj[prop] = value;
        callback(`${prop}가 [#{prev}] >> [${value}] 로 변경되었습니다. `);
      }
      return true;
    },
  });
  return proxy;
}
const a = {
  형규: "솔로",
};
const b = createReactiveObject(a, console.log);
b.형규 = "솔로";
b.형규 = "커플";
// 형규가 [솔로] >> [커플]로 변경되었습니다.