본문 바로가기
  • 프론트엔드 개발자 세오세오 | Frontend dev Seo
Learn to Code

[디자인패턴] 자바스크립트로 옵저버패턴(Observer pattern) 구현하기

by CEOSEO 2024. 6. 24.
728x90
반응형

옵저버 패턴은 Subject라 불리는 객체가 옵저버라 불리는 디펜던트들을 유지하고, 상태 변화가 생겼을 때 이들에게 상황을 알리는 경우를 의미한다. 이 패턴은 한 객체의 액션을 다른 객체에 업데이트시켜야하는 상황에서 특히 유용하다.

 

아래는 자바스크립트로 구현한 예시이다. UI나 로그 시스템 객체는 Character 객체의 변화를 알고 싶어한다. Character의 HP가 바뀌면, 옵저버인 UI가 이에 대해 알아야하고, 변경된 HP를 보여줘야 한다.

 

728x90

 

1. Subject 인터페이스를 만들고, 이걸 사용해서 Character 클래스로 만든다

class Subject {
  addObserver(observer) {
    throw new Error("You must implement addObserver method!")
  }
  
  removeObserver(observer) {
    throw new Error("You must implement the removeObserver method!")
  }
  
  notifyObservers() {
    throw new Error("You must implement the notifyObservers method!")
  }
}

 

class Character extends Subject {
  contructor (name, health) {
	  super()
    this.name = name
    this.health = health
    this.observers = []
  }
  
  addObserver(observer) {
    if (observer instanceof Observer && observer instanceof DisplayEment) {
      this.observers.push(observer)
    } else {
      throw new Error("Observer must implement both Observer and DisplayElement interfaces!!")
    }

  }
  
  removeObservers(observer) {
    this.observers = this.observers.filter(obs => obs !== observer)
  }
  
  notifyObservers() {
    this.observers.forEach(observer => observer.update(this))
  }
  
  setHealth(newHealth) {
    console.log(`${this.name} health changed from ${this.health} to ${newHealth}`)
    this.health = newHealth
    this.notifyObservers()
  }
}

 

 

2. Observer와 DisplayElement 인퍼테이스를 만든다

class Observer {
  update(subject) {
    throw new Error("You must implement the update method!")
  }
}

class DisplayElement {
  display() {
    throw new Error("You must implement the display method!)
  }
}

 

 

두 인터페이스를 사용해 UIDisplay 클래스와 Logger 클래스를 만든다.

class UIDisplay extends Observer {
  constructor() {
    super()
    this.character = null
  }
  
  update(character) {
    this.character = character
    this.display()    
  }
  
  display() {
    console.log(`UI display: ${character.name}'s health is now ${character.health}`)
  }
}

Object.setPrototypeOf(UIDisplay.prototype, DisplayElement.prototype)


class Logger extends Observer {
  constructor() {
    super()
    this.character = null
  }
  
  update(character) {
    this.character = character
    this.display()
  }
  
  display() {
    console.log(`Logger: ${character.name}'s health changed to ${character.health}`)
  }
}

Object.setPrototypeOf(Logger.prototype, DisplayElement.prototype)

 

 

Object.setPrototypeOf는 지정한 객체의 프로토타입을 다른 객체 또는 null로 설정하는 메소드이다. 이 메소드는 객체의 상속을 변경한다 .

 

예시 코드의 맥락에서 Object.setPrototypeOf는 다수의 상속을 가져가기 위해 사용되었다. 이를 통해 하나의 “인터페이스” 또는 프로토타입으로부터 상속이 가능해진다. 자바스크립트는 직접적으로 다수의 상속을 지원하지 않으나, 수동으로 프로토타입 체인을 설정해서 비슷한 효과를 얻을 수 있다.

 

자바스크립트 클래스와 객체는 프로토타입으로 만들어진 것들이다. 디폴트로, 각 객체는 다른 객체로 연결되는 내부 프로토타입 속성 ( [[Prototype]] )을 갖고 있다. 이러한 프로토타입 체인을 통해, 자바스크립트는 상속을 구현한다.

 

본 예시에선, UIDisplay와 Logger 클래스가 마치 Observer와 DisplayElement 인터페이스 두 개 모두를 상속받게 하고 싶었다. 이를 위해선, Object.setPrototypeOf를 사용해서 수동으로 UIDisplay와 Logger 클래스의 프로토타입을 Observer와 DisplayElement 모두에 설정해야 한다.

 

 

3. 사용한다!!

const jonSnow = new Character('Jon', 100)

const uiDisplay = new UIDisplay()
const logger = new Logger()

hero.addObserver(uiDisplay)
hero.addObserver(logger)

hero.setHealth(90)
hero.setHealth(75)

 

728x90
반응형

댓글