Web/JS

[JS] 디자인 패턴(Design Pattern)_옵저버 패턴(Observer Pattern)

메바동 2023. 2. 25. 00:19
728x90

"JavaScript Design Pattern" 포스팅은 ChatGPT에게 "Essential design patterns for JavaScript developers to learn"로 질문한 뒤 나온 내용에 대해 정리하는 포스팅입니다.

 


 

1. 옵저버 패턴(Observer Pattern)

옵저버 패턴은 주체(Subject)로 불리는 객체가 옵저버라고 하는 종속 객체의 목록을 유지 관리하고, 일반적으로 메서드 중 하나를 호출하여 상태 변경을 자동으로 알리는 디자인 패턴이다.

옵저버 패턴은 객체 간의 느슨한 결합을 허용하여 서로의 구현에 대해 세부 사항을 알 필요 없이 상호 작용할 수 있다.

옵저버 패턴은 주체와 주체의 옵저버 간의 일대다 관계를 정의한다. 주체가 변경되면 모든 옵저버에게 알림이 전달되고 자동으로 업데이트된다.

 

옵저버 패턴은 게시/구독 패턴(Publish/Subscribe Pattern)으로도 알려져 있는데, 게시자가 메시지를 수신하는 데 관심을 표명한 모든 구독자에게 메시지를 브로드캐스트 하는 메시징 시스템에서 종종 사용되기 때문이다.

 

옵저버 패턴은 객체의 상태가 자주 변경되고 다른 객체가 그에 따라 반응해야 하는 이벤트 중심 아키텍처나 애플리케이션에서 자주 사용된다.

몇 가지 일반적인 사용 사례로는 사용자 상호작용에 따른 UI 업데이트, 실시간 데이터 스트리밍, 네트워크 통신 등이 있다.

 

JavaScript에서 옵저버 패턴은 다음과 같은 여러 기술을 사용하여 구현할 수 있다.

 

  1. 콜백 함수: 대상 객체는 대상의 상태가 변경될 때마다 호출되는 콜백 함수 목록을 유지 관리한다.
  2. 이벤트 리스너: 주체 객체가 이벤트를 방출하고, 옵저버 객체는 해당 이벤트를 수신하고 그에 따라 응답한다.
  3. Promises: 주체 객체는 주체의 상태가 변경되면 resolve 되는 promise를 반환한다.

 

다음은 콜백 함수를 사용하여 옵저버 패턴을 구현한 예이다:

 

// 주제 객체 정의
const subject = {
  state: 'initial state',
  observers: [],

  // 옵저버를 추가하는 메서드
  addObserver: function(observer) {
    this.observers.push(observer);
  },

  // 옵저버를 제거하는 메서드
  removeObserver: function(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  },

  // 옵저버에게 알림을 보내는 메서드
  notifyObservers: function() {
    this.observers.forEach(observer => observer(this.state));
  },

  // 상태를 업데이트하는 메서드
  setState: function(newState) {
    this.state = newState;
    this.notifyObservers();
  }
};

// 옵저버 함수 정의
const observer1 = state => console.log(`Observer 1: ${state}`);
const observer2 = state => console.log(`Observer 2: ${state}`);

// 주체에 옵저버 추가
subject.addObserver(observer1);
subject.addObserver(observer2);

// 주체의 상태 업데이트
subject.setState('new state');

// Output:
// Observer 1: new state
// Observer 2: new state

// 주체로부터 observer2 제거
subject.removeObserver(observer2);

// 주체의 상태 업데이트
subject.setState('another new state');

// Output:
// Observer 1: another new state

 

이 예제에서 주체 객체는 옵저버 배열에 옵저버 함수 목록을 유지한다.

setState() 메서드가 호출되면 주체 객체의 상태 속성을 업데이트하고 notifyObserver() 메서드를 호출하여 옵저버 배열을 반복하고 새 상태를 인수로 사용하여 각 옵저버 함수를 호출한다.

observer1 및 observer2 함수는 주체 객체에 옵저버로 추가되며, setState() 메서드가 호출될 때 상태 변경에 대한 알림을 받는다.

observer2 함수는 나중에 removeObserver() 메서드를 사용하여 주체 객체에서 제거되므로 더 이상 상태 변경에 대한 알림을 받지 않는다.

 

옵저버 패턴의 한 가지 장점은 개체 간의 느슨한 결합을 촉진한다는 점이다.

옵저버는 등록 및 알림을 위해 노출되는 인터페이스 외에는 주체에 대해 아무것도 알 필요가 없다. 따라서 주체나 다른 옵저버에게 영향을 주지 않고 옵저버를 쉽게 추가하거나 제거할 수 있다.

 

전반적으로 옵저버 패턴은 변화하는 요구 사항과 조건에 쉽게 적응할 수 있는 분리된 이벤트 중심 시스템을 구축하기 위한 강력한 기술이다.

 

 

 

2. Observer Pattern 연습 코드

ChatGPT에게 "Give me a challenge to practice the Observer Pattern in JavaScript."라고 질문한 뒤 나온 문제와 그에 대한 답이다.

 

 

스켈레톤 코드는 다음과 같다.

 

class Observer {
  // TODO: Implement the Observer class with a subscribe() method and a notify() method
}

class Subject {
  constructor() {
    this.observers = [];
  }

  registerObserver(observer) {
    // TODO: Implement the registerObserver() method
  }

  unregisterObserver(observer) {
    // TODO: Implement the unregisterObserver() method
  }

  notifyObservers() {
    // TODO: Implement the notifyObservers() method
  }
}

// Usage
const subject = new Subject();

// Create two observers
const observer1 = new Observer();
const observer2 = new Observer();

// Register the observers with the subject
subject.registerObserver(observer1);
subject.registerObserver(observer2);

// Notify the observers
subject.notifyObservers();

// Unregister one observer
subject.unregisterObserver(observer1);

// Notify the remaining observer
subject.notifyObservers();

 

이를 구현한 코드는 다음과 같다.

 

class Observer {
  constructor() {
    this.subscribers = [];
  }

  subscribe(callback) {
    this.subscribers.push(callback);
  }

  notify() {
    this.subscribers.forEach(callback => callback());
  }
}

class Subject {
  constructor() {
    this.observers = [];
  }

  registerObserver(observer) {
    this.observers.push(observer);
  }

  unregisterObserver(observer) {
    this.observers = this.observers.filter(ob => ob !== observer);
  }

  notifyObservers() {
    this.observers.forEach(observer => observer.notify());
  }
}

// Usage
const subject = new Subject();

// Create two observers
const observer1 = new Observer();
const observer2 = new Observer();

observer1.subscribe(data => console.log('observer1 received notify.'));
observer2.subscribe(data => console.log('observer2 received notify.'));

// Register the observers with the subject
subject.registerObserver(observer1);
subject.registerObserver(observer2);

// Notify the observers
subject.notifyObservers();
// output
// observer1 received notify.
// observer2 received notify.

// Unregister one observer
subject.unregisterObserver(observer1);

// Notify the remaining observer
subject.notifyObservers();
// output
// observer2 received notify.
728x90