STUDY/CS

[디자인 패턴] 옵서버 패턴 (Observer Pattern) / 자바스크립트, 프록시 객체

ez1n 2023. 1. 10. 00:35

옵서버 패턴 (Observer Pattern)

관찰자가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 객체에 상속되어 있는 다른 객체들(옵서버)에게 전달하는 패턴

 

  • 이벤트 기반 시스템 / MVC 패턴에 사용됨
  • 주로 분산된 시스템 간에 이벤트를 생성 / 발행 (Publish)하고, 이를 수신(Subscribe)해야 할 때 사용
  • 관찰자 : 객체의 상태 변화를 감지하는 관찰자
  • 옵서버 : 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 '추가 변화 사항'이 생기는 객체들

 

예제

  • 옵서버 생성
// 옵서버 클래스 (base)
class Observer {
  constructor(user) {
    this.user = user.name;
  }
  notify(message) {
    console.log(`User1 님이 ${this.user}님께 메시지를 보냈습니다. : ${message}`);
  }
}

class Observer1 extends Observer {
  constructor() {
    super({ name: 'follower1' });
  }
}

class Observer2 extends Observer {
  constructor() {
    super({ name: 'follower2' });
  }
}

class Observer3 extends Observer {
  constructor() {
    super({ name: 'follower3' });
  }
}

 

  • 관찰 대상 클래스 생성
class Subject {
  constructor() {
    this.followers = []; // 객체의 변화를 감지할 옵저버 리스트
  }

  // 감시할 옵서버 구독
  subscribe(follower) {
    this.followers.push(follower);
  }

  // 옵서버 구독 삭제
  unsubscribe(removedChild) {
    this.followers = this.followers.filter(follower => follower !== removedChild);
  }

  // 알림
  notify(message) {
    for (let follower of this.followers) {
      // 객체 상태 변경에 따른 옵저버의 로직 실행
      follower.notify(message);
    }
  }
}

// User1 클래스에서 옵서버들 관리
class User1 extends Subject { }

 

  • 옵서버 구독
// 관찰 대상 객체 생성
const user1 = new User1();

// 옵서버 객체 생성
const follower1 = new Observer1();
const follower2 = new Observer2();
const follower3 = new Observer3();

// 구독
user1.subscribe(follower1);
user1.subscribe(follower2);
user1.subscribe(follower3);

user1.notify('공지합니다.');

/** console.log
 * User1 님이 follower1님께 메시지를 보냈습니다. : 공지합니다.
 * User1 님이 follower2님께 메시지를 보냈습니다. : 공지합니다.
 * User1 님이 follower3님께 메시지를 보냈습니다. : 공지합니다.
 */

 

장점

  1. 느슨한 결합 → 상호 의존성 최소화
  2. polling 방지
    • polling : 상태를 주기적으로 확인하고 조건을 만족할 시 자료처리를 하는 방식
    • 짧은 주기 : 부하 발생, 긴 주기 : 실시간성 떨어짐

단점

  1. 코드 간 연결 파악이 어려울 수 있음
  2. 옵저버가 복잡한 경우 성능에 문제가 발생할 수 있음

 

프록시 객체 이용하여 구현

# Proxy 객체

  • 기본적인 동작 (속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사용 (동작을 가로챌 수 있는 객체)
  • 기존 객체의 속성이나 메소드 덮어쓸 수 있음
  • 매개변수 : target, handler

# Proxy 메소드

  1. get() : 속성과 함수에 대한 접근을 가로챔 - target, prop, receiver
  2. has() : in 연산자의 사용을 가로챔
  3. set() : 속성에 대한 접근을 가로챔 - target, prop, val, receiver

예제

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.형규 = "커플"; // 형규가 [솔로] >> [커플] 로 변경되었습니다.
  • 객체의 속성이나 메소드 변화 등을 감지하고 옵서버들에게 전달함