본문 바로가기
JavaScript 문법 시리즈

[JavaScript 문법] 21일차: 디자인 패턴 심화

by cogito21_js 2024. 8. 21.
반응형

옵저버 패턴

옵저버 패턴(Observer Pattern)은 객체의 상태 변화를 관찰하는 옵저버들이 그 변화를 감지하고, 필요에 따라 행동하는 패턴입니다. 주로 이벤트 핸들링 시스템에서 많이 사용됩니다.

예제

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

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

  removeObserver(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

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

class Observer {
  constructor(name) {
    this.name = name;
  }

  update(message) {
    console.log(`${this.name} received message: ${message}`);
  }
}

const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers('Hello, Observers!');
// 출력:
// Observer 1 received message: Hello, Observers!
// Observer 2 received message: Hello, Observers!

전략 패턴

전략 패턴(Strategy Pattern)은 여러 알고리즘을 정의하고, 각 알고리즘을 캡슐화하여 상호 교환 가능하게 만드는 패턴입니다. 이는 알고리즘을 사용하는 클라이언트 코드가 독립적으로 알고리즘을 변경할 수 있게 합니다.

예제

class PaymentStrategy {
  pay(amount) {
    throw new Error('pay() must be implemented');
  }
}

class CreditCardPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} using Credit Card`);
  }
}

class PayPalPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} using PayPal`);
  }
}

class ShoppingCart {
  constructor() {
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  calculateTotal() {
    return this.items.reduce((total, item) => total + item.price, 0);
  }

  pay(strategy) {
    const amount = this.calculateTotal();
    strategy.pay(amount);
  }
}

const cart = new ShoppingCart();
cart.addItem({ name: 'Laptop', price: 1000 });
cart.addItem({ name: 'Mouse', price: 50 });

cart.pay(new CreditCardPayment());
// 출력: Paid 1050 using Credit Card

cart.pay(new PayPalPayment());
// 출력: Paid 1050 using PayPal

데코레이터 패턴

데코레이터 패턴(Decorator Pattern)은 객체에 동적으로 새로운 기능을 추가할 수 있게 하는 패턴입니다. 이는 서브클래스를 생성하지 않고도 객체의 기능을 확장할 수 있게 합니다.

예제

class Coffee {
  cost() {
    return 5;
  }
}

class MilkDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }

  cost() {
    return this.coffee.cost() + 1;
  }
}

class SugarDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }

  cost() {
    return this.coffee.cost() + 0.5;
  }
}

let myCoffee = new Coffee();
console.log(myCoffee.cost()); // 5

myCoffee = new MilkDecorator(myCoffee);
console.log(myCoffee.cost()); // 6

myCoffee = new SugarDecorator(myCoffee);
console.log(myCoffee.cost()); // 6.5

옵저버 패턴의 장점과 단점

장점

  1. 느슨한 결합: 주체와 옵저버 간의 결합을 느슨하게 유지하여 코드의 유연성을 높입니다.
  2. 확장성: 새로운 옵저버를 쉽게 추가할 수 있습니다.

단점

  1. 메모리 누수: 옵저버를 제거하지 않으면 메모리 누수가 발생할 수 있습니다.
  2. 순환 참조: 주체와 옵저버 간의 순환 참조가 발생할 수 있습니다.

전략 패턴의 장점과 단점

장점

  1. 유연성: 알고리즘을 쉽게 교환할 수 있습니다.
  2. 캡슐화: 알고리즘을 캡슐화하여 클라이언트 코드가 알고리즘의 구현 세부 사항을 몰라도 됩니다.

단점

  1. 복잡성 증가: 알고리즘이 많아지면 클래스 수가 증가하여 복잡해질 수 있습니다.
  2. 전략 선택: 적절한 전략을 선택하는 로직이 필요할 수 있습니다.

데코레이터 패턴의 장점과 단점

장점

  1. 유연성: 객체의 기능을 동적으로 확장할 수 있습니다.
  2. 단일 책임 원칙: 기능을 여러 클래스로 분리하여 단일 책임 원칙을 준수할 수 있습니다.

단점

  1. 복잡성 증가: 데코레이터가 많아지면 코드의 복잡성이 증가할 수 있습니다.
  2. 성능 오버헤드: 데코레이터를 여러 개 중첩하면 성능 오버헤드가 발생할 수 있습니다.

결론

디자인 패턴은 소프트웨어 설계에서 자주 발생하는 문제를 해결하기 위한 재사용 가능한 솔루션입니다. 이번 글에서는 옵저버 패턴, 전략 패턴, 데코레이터 패턴에 대해 배웠습니다. 이러한 패턴을 이해하고 적절히 활용하면 코드의 재사용성과 유지보수성을 크게 향상시킬 수 있습니다. 다음 글에서는 콜백 함수에 대해 알아보겠습니다.

다음 글에서 만나요!

 

반응형