디자인 패턴

hwisaac·2023년 2월 23일
0

리팩토링

목록 보기
4/6

1. DRY ( Don't Repeat Yourself )

코드 중복을 최소화하여 코드의 가독성과 유지 보수성을 향상시키는 개발 원칙입니다.

가장 필수적인 코딩 패턴으로 "Don't Repeat Yourself" 원리입니다.
DRY 원리는 코드가 중복되지 않도록 코드를 작성해야 한다는 코딩 철학입니다.

같은 코드 블록이나 로직이 여러 번 반복되는 경우, 이를 하나의 함수나 모듈로 추출하고 재사용함으로써 중복을 제거합니다. 이를 통해 코드의 일관성과 유연성을 높일 수 있습니다.

중복을 방지함으로써 코드를 더 유지 관리하기 쉽고 이해하기 쉬우며 버그 발생률을 줄일 수 있습니다. 또한 작성해야 하는 코드의 양을 줄이고 나중에 코드를 쉽게 변경할 수 있습니다.

JavaScript에서 DRY 패턴을 적용하는 예시를 보여드리겠습니다. 예를 들어, 다음과 같은 중복 코드가 있다고 가정해 봅시다.

let width = 10;
let height = 20;
let area = width * height;
console.log(area);

width = 15;
height = 25;
area = width * height;
console.log(area);

위 코드에서는 widthheight의 값을 변경한 후에 다시 area를 계산하고 출력하고 있습니다. 이러한 중복을 제거하고 DRY 패턴을 적용하려면 다음과 같이 함수를 만들 수 있습니다.

function calculateArea(width, height) {
  const area = width * height;
  return area;
}

let width = 10;
let height = 20;
console.log(calculateArea(width, height));

width = 15;
height = 25;
console.log(calculateArea(width, height));

위 코드에서는 calculateArea 함수를 만들어서 widthheight의 값을 인자로 전달하고, area를 계산하고 반환합니다. 이를 통해 area를 계산하는 중복을 제거하고, 함수를 호출하여 area를 계산하고 출력할 수 있습니다.

2. Singleton Pattern

클래스의 인스턴스화를 하나의 개체로 제한하고 해당 개체에 대한 글로벌 액세스 지점을 제공하는 설계 패턴입니다.

let instance = null;

class MyClass {
  constructor() {
    if (!instance) {
      instance = this;
    }

    return instance;
  }

  sayHello() {
    console.log('Hello!');
  }
}

const firstInstance = new MyClass();
const secondInstance = new MyClass();

console.log(firstInstance === secondInstance); // true

firstInstance.sayHello(); // "Hello!"
secondInstance.sayHello(); // "Hello!"

이 코드에서 MyClass는 오직 하나의 인스턴스만 가질 수 있습니다.
그리고 정적 변수인 instanace 에다 저장해놓았습니다.

생산자에서, 클래스의 인스턴스가 이미 생성되었는지 체크하고, 만약 그렇다면 우리는 해당 instance 를 리턴합니다. 아니라면 thisinstance 에 할당합니다.

그러면 MyClass 의 인스턴스를 생성할때마다, 우리는 항상 동일한 인스턴스를 받게 됩니다.

이 패턴은 데이터베이스에 대한 연결이 하나만 있는지 확인하려는 경우나 React에서 전역 상태 개체를 만들려는 경우와 같이 만들 수 있는 클래스의 인스턴스 수를 제한하려는 경우에 유용합니다.

3. Factory Pattern

superclass에서 객체를 만들 수 있는 인터페이스를 제공하지만 하위 클래스에서 만들 객체의 유형을 변경할 수 있도록 하는 디자인 패턴입니다.

팩토리 패턴은 객체를 생성하는 코드를 클라이언트로부터 분리하여, 같은 인터페이스를 가진 다양한 타입의 객체를 생성할 수 있는 패턴입니다. 팩토리 메소드 패턴과 추상 팩토리 패턴이 있습니다.

아래는 JavaScript에서 팩토리 메소드 패턴을 사용한 예제입니다.

// Button 객체 생성
class Button {
  constructor(width, height, background) {
    this.width = width;
    this.height = height;
    this.background = background;
  }

  render() {
    const button = document.createElement('button');
    button.style.width = `${this.width}px`;
    button.style.height = `${this.height}px`;
    button.style.background = this.background;
    return button;
  }
}

// Button 팩토리
class ButtonFactory {
  createButton(width, height, background) {
    return new Button(width, height, background);
  }
}

// 생성된 Button 객체를 body 태그에 추가
const body = document.querySelector('body');
const buttonFactory = new ButtonFactory();

const redButton = buttonFactory.createButton(100, 50, 'red');
body.appendChild(redButton.render());

const blueButton = buttonFactory.createButton(200, 100, 'blue');
body.appendChild(blueButton.render());

위 예제에서 Button 객체를 생성하기 위해 Button 클래스를 정의하였습니다.

그리고 ButtonFactory 클래스에서 createButton 메소드를 이용하여, 인자로 넘겨준 값을 이용하여 Button 객체를 생성합니다.

ButtonFactory 클래스의 createButton 메소드를 호출할 때 생성하고자 하는 Button의 타입을 인자로 넘겨주면, 해당 타입의 Button 객체를 생성하여 반환합니다.

마지막으로, 생성한 Button 객체를 body 태그에 추가합니다.

이 예제는 단순한 팩토리 패턴의 구현이지만, 팩토리 패턴을 이용하면 인터페이스와 구현을 분리하여 확장성이 좋은 코드를 작성할 수 있습니다.

4. Observer Pattern

개체가 종속자 목록을 유지하고 종속자가 변경될 때 자동으로 알림을 받는 디자인 패턴입니다.

Observer pattern은 객체지향 디자인 패턴중 하나로 객체 간의 일대다 의존성 관계를 정의하는 행위 패턴입니다.

이 패턴은 하나의 객체(주제)의 상태가 변경될 때 그와 의존관계에 있는 다른 객체들(관찰자)에게 모두 알려주고, 자동으로 내용을 갱신하도록 하는 방식으로 작동합니다.

예를 들어, 사용자가 어떤 작업을 수행하는 동안 특정 상태에 대한 알림을 받아야하는 경우가 있습니다.

이때 사용자는 객체(주제)의 변경 내용에 대한 관심을 가지며, 알림을 받기를 원하는 객체들(관찰자)을 등록합니다.

사용자가 변경 내용을 수행하면, 객체(주제)는 등록된 모든 관찰자들에게 자동으로 알립니다.

이제 이 패턴을 자바스크립트 예제로 설명해보겠습니다.

class Subject {
  constructor() {
    this.observers = []; // 관찰자 배열 초기화
  }

  addObserver(observer) {
    this.observers.push(observer); // 관찰자를 배열에 추가
  }

  removeObserver(observer) {
    this.observers = this.observers.filter((obs) => obs !== observer); // 관찰자를 배열에서 제거
  }

  notify(data) {
    this.observers.forEach((observer) => observer.update(data)); // 모든 관찰자에게 알림을 보냄
  }
}

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

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

const subject = new Subject(); // 주제 생성
const observer1 = new Observer('Observer 1'); // 관찰자 1 생성
const observer2 = new Observer('Observer 2'); // 관찰자 2 생성

subject.addObserver(observer1); // 관찰자 1 등록
subject.addObserver(observer2); // 관찰자 2 등록

subject.notify('Hello World!'); // 모든 관찰자에게 알림을 보냄

위 예제에서 Button 객체를 생성하기 위해 Button 클래스를 정의하였습니다.

그리고 ButtonFactory 클래스에서 createButton 메소드를 이용하여, 인자로 넘겨준 값을 이용하여 Button 객체를 생성합니다.

ButtonFactory 클래스의 createButton 메소드를 호출할 때 생성하고자 하는 Button의 타입을 인자로 넘겨주면, 해당 타입의 Button 객체를 생성하여 반환합니다. 마지막으로, 생성한 Button 객체를 body 태그에 추가합니다.

이 예제는 단순한 팩토리 패턴의 구현이지만, 팩토리 패턴을 이용하면 인터페이스와 구현을 분리하여 확장성이 좋은 코드를 작성할 수 있습니다.

자바스크립트에서는 옵저버 패턴을 구현하기 위해 주로 이벤트 리스너를 활용합니다. 예를 들어, 아래 코드는 버튼 클릭 이벤트를 감시하는 옵저버를 구현한 것입니다.

// 버튼 엘리먼트
const button = document.querySelector('#myButton');

// 옵저버 객체
const observer = {
  // 상태 변화에 대한 알림을 받을 콜백 함수
  update: function() {
    console.log('Button clicked');
  }
};

// 버튼 클릭 이벤트에 옵저버 등록
button.addEventListener('click', observer.update);

위 코드에서는 update 함수를 갖는 객체를 옵저버로 활용했습니다. 버튼 클릭 이벤트에 대해 이 옵저버 객체의 update 함수가 실행됩니다. 이제 버튼이 클릭될 때마다 'Button clicked'이라는 메시지가 콘솔에 출력됩니다.

이와 같은 방식으로 옵저버 패턴을 활용하면 상태의 변화에 대한 알림을 효과적으로 관리할 수 있습니다.

단, 이벤트 리스너 등록 시 주의해야 할 점은, 이벤트 발생 후에도 이벤트 리스너가 계속 유지되는 경우가 있으므로, 필요 없어진 옵저버는 반드시 등록 해제해주어야 합니다.

5. Strategy Pattern

런타임에 알고리즘의 동작을 선택할 수 있도록 하는 설계 패턴입니다.

Strategy Pattern은 행위 패턴(behavioral pattern) 중 하나로, 런타임 시 동적으로 알고리즘을 선택할 수 있게 하는 디자인 패턴입니다.

이 패턴은 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경할 수 있도록 해주며, 유사한 알고리즘을 가진 여러 개의 클래스가 있을 때, 이들을 하나의 추상 클래스를 상속받아 구현하는 방식으로 코드를 작성합니다.

그리고 이들 알고리즘을 캡슐화하여 동적으로 교체할 수 있는 구조를 만들어냅니다.

JavaScript에서는 함수를 이용하여 구현할 수 있습니다. 예를 들어, 가격을 계산하는데에 적용될 세금이나 할인 쿠폰 등의 요인이 다양하게 적용될 수 있는 경우, 가격 계산 로직을 분리하여 독립적으로 관리할 수 있습니다.

class PriceCalculator {
  constructor(discountStrategy, taxStrategy) {
    this.discountStrategy = discountStrategy;
    this.taxStrategy = taxStrategy;
  }

  calculate(price) {
    const priceAfterDiscount = this.discountStrategy(price);
    const totalPrice = this.taxStrategy(priceAfterDiscount);
    return totalPrice;
  }

  setDiscountStrategy(discountStrategy) {
    this.discountStrategy = discountStrategy;
  }

  setTaxStrategy(taxStrategy) {
    this.taxStrategy = taxStrategy;
  }
}

// 할인 전략 구현
const tenPercentDiscount = (price) => price * 0.9;
const twentyPercentDiscount = (price) => price * 0.8;

// 세금 계산 전략 구현
const taxIncluded = (price) => price * 1.1;
const noTaxIncluded = (price) => price;

// 가격 계산기 인스턴스 생성
const priceCalculator = new PriceCalculator(tenPercentDiscount, taxIncluded);

// 가격 계산하기
console.log(priceCalculator.calculate(1000)); // 990

// 할인 전략 변경
priceCalculator.setDiscountStrategy(twentyPercentDiscount);

// 가격 계산하기
console.log(priceCalculator.calculate(1000)); // 800

// 세금 계산 전략 변경
priceCalculator.setTaxStrategy(noTaxIncluded);

// 가격 계산하기
console.log(priceCalculator.calculate(1000)); // 800

위 예제에서 PriceCalculator 클래스는 discountStrategy와 taxStrategy 매개변수를 받아서 calculate 메소드 내에서 이들을 이용하여 가격을 계산합니다.

또한, setDiscountStrategysetTaxStrategy 메소드를 통해 할인 전략과 세금 계산 전략을 동적으로 변경할 수 있습니다.

이러한 방식으로, 런타임 시 동적으로 다양한 전략을 적용할 수 있게 됩니다.

6. Decorator Pattern

동일한 클래스의 다른 개체의 동작에 영향을 미치지 않고 정적 또는 동적으로 개별 개체에 동작을 추가할 수 있는 디자인 패턴입니다.

Decorator pattern은 객체의 기능을 동적으로 추가 및 삭제할 수 있도록 하는 구조적 디자인 패턴입니다. 이 패턴을 사용하면 기존 객체에 새로운 기능을 추가하거나 런타임에 기존 기능을 삭제할 수 있습니다.

예를 들어, 렌더링 엔진에 라운드 코너, 그림자, 테두리 등의 스타일을 추가할 수 있는 데코레이터를 만들 수 있습니다. 이러한 데코레이터는 새로운 스타일을 적용하는 메소드를 포함하는 스타일 객체에 대한 참조를 갖습니다. 그런 다음, 이러한 데코레이터를 이용하여 객체의 스타일을 추가하거나 제거할 수 있습니다.

JavaScript에서 Decorator pattern을 구현할 때는 클래스의 프로토타입에 새로운 메소드를 추가하거나 덮어쓰는 것으로 시작할 수 있습니다. 예를 들어, 다음과 같은 Component 클래스가 있다고 가정해 봅시다.

class Component {
  constructor() {
    this.price = 0;
  }

  getPrice() {
    return this.price;
  }
}

그리고 이 클래스를 상속받아 새로운 데코레이터 클래스들을 만들어 봅시다.

class Decorator extends Component {
  constructor(component) {
    super();
    this.component = component;
  }

  getPrice() {
    return this.component.getPrice() + this.price;
  }
}

class VatDecorator extends Decorator {
  constructor(component) {
    super(component);
    this.price = 10;
  }
}

class DiscountDecorator extends Decorator {
  constructor(component) {
    super(component);
    this.price = -5;
  }
}

이제 이 데코레이터 클래스를 사용하여 Component 객체의 기능을 동적으로 수정할 수 있습니다.

const component = new Component();
const vatDecorator = new VatDecorator(component);
const discountDecorator = new DiscountDecorator(vatDecorator);

console.log(discountDecorator.getPrice()); // 5

위의 코드에서 Component 클래스는 기본적인 기능을 담당하고, Decorator 클래스는 Component 객체를 상속받아 새로운 기능을 추가합니다. VatDecoratorDiscountDecorator 클래스는 Decorator 클래스를 상속받아 새로운 기능을 추가하며, 이러한 데코레이터를 사용하여 Component 객체의 기능을 동적으로 수정할 수 있습니다.

7. Command Pattern

객체가 나중에 메서드를 호출하는 데 필요한 모든 정보를 표시하고 캡슐화하는 데 사용되는 설계 패턴입니다.

Command Pattern은 객체 지향 디자인 패턴 중 하나로, 요청을 객체로 캡슐화하여 여러 기능을 구현하는 방법입니다. 이 패턴은 요청을 수행하는 객체와 요청을 발행하는 객체를 분리함으로써, 실행되는 기능을 유연하게 변경하고 관리할 수 있습니다.

Command pattern을 적용하는 가장 일반적인 경우 중 하나는 실행 취소 및 다시 실행을 지원하는 애플리케이션을 작성할 때입니다. 이를 위해 Command 객체는 실행 취소와 다시 실행 메서드를 제공해야 합니다.

Command Pattern은 대부분의 객체 지향 언어에서 쉽게 구현될 수 있습니다. 아래는 JavaScript로 구현한 Command Pattern 예시입니다.

class Calculator {
  constructor() {
    this.value = 0;
    this.history = [];
  }

  executeCommand(command) {
    this.value = command.execute(this.value);
    this.history.push(command);
  }

  undo() {
    const command = this.history.pop();
    this.value = command.undo(this.value);
  }
}

class AddCommand {
  constructor(valueToAdd) {
    this.valueToAdd = valueToAdd;
  }

  execute(currentValue) {
    return currentValue + this.valueToAdd;
  }

  undo(currentValue) {
    return currentValue - this.valueToAdd;
  }
}

class MultiplyCommand {
  constructor(valueToMultiply) {
    this.valueToMultiply = valueToMultiply;
  }

  execute(currentValue) {
    return currentValue * this.valueToMultiply;
  }

  undo(currentValue) {
    return currentValue / this.valueToMultiply;
  }
}

이 예제에서는 Calculator 클래스에 executeCommand()undo() 메서드가 있습니다. AddCommandMultiplyCommand 클래스는 모두 Command 인터페이스를 구현하며, execute()undo() 메서드를 갖습니다. execute() 메서드는 값을 계산하고, undo() 메서드는 계산을 실행하기 전으로 되돌립니다. Calculator 클래스의 executeCommand() 메서드는 주어진 Command 객체의 execute() 메서드를 호출하여 값을 업데이트하고, 해당 Command 객체를 기록합니다. undo() 메서드는 마지막으로 기록된 Command 객체를 추출하고, 해당 객체의 undo() 메서드를 호출하여 값을 되돌립니다.

이렇게 하면 다음과 같은 방식으로 계산기를 사용할 수 있습니다.

const calculator = new Calculator();

const addCommand = new AddCommand(10);
calculator.executeCommand(addCommand);

const multiplyCommand = new MultiplyCommand(2);
calculator.executeCommand(multiplyCommand);

console.log(calculator.value); // 20

calculator.undo();
console.log(calculator.value); // 10

이 예제에서는 Calculator 클래스의 인스턴스를 만들고 AddCommandMultiplyCommand 인스턴스를 만들어 각각의 executeCommand() 메서드를 호출하여 값을 변경합니다. 마지막으로, undo() 메서드를 호출하여 마지막 계산을 실행하기 전으로 되돌릴 수 있습니다.

8. Template Method Pattern

기본 알고리즘이 슈퍼 클래스에서 정의되지만 특정 단계가 하위 클래스에 의해 구현되도록 남겨지는 설계 패턴입니다.

Template Method Pattern은 객체 지향 프로그래밍에서 사용되는 디자인 패턴 중 하나로, 하위 클래스에서 알고리즘의 일부를 구현하도록 허용하면서 전체 알고리즘 구조를 정의하는 데 사용됩니다.

이 패턴은 상위 클래스에서 알고리즘의 기본적인 구조를 정의하고, 하위 클래스에서 구체적인 구현을 처리합니다. 이렇게 하면 알고리즘이 변경되더라도 전체 구조를 유지할 수 있으며, 코드 재사용성이 높아집니다.

예를 들어, 다음은 template method pattern을 사용하여 구현된 JavaScript 코드입니다.

class AbstractClass {
  templateMethod() {
    this.primitiveOperation1();
    this.primitiveOperation2();
  }
  
  primitiveOperation1() {
    console.log("AbstractClass.primitiveOperation1");
  }
  
  primitiveOperation2() {
    console.log("AbstractClass.primitiveOperation2");
  }
}

class ConcreteClass extends AbstractClass {
  primitiveOperation1() {
    console.log("ConcreteClass.primitiveOperation1");
  }
  
  primitiveOperation2() {
    console.log("ConcreteClass.primitiveOperation2");
  }
}

const clientCode = () => {
  const concreteClass = new ConcreteClass();
  concreteClass.templateMethod();
}

clientCode(); // 출력: ConcreteClass.primitiveOperation1
             //       ConcreteClass.primitiveOperation2

위 예제에서 AbstractClass는 알고리즘의 기본적인 구조를 정의합니다. templateMethod() 메서드는 primitiveOperation1()primitiveOperation2() 메서드를 순차적으로 호출합니다.

ConcreteClassAbstractClass를 상속받아 primitiveOperation1()primitiveOperation2() 메서드를 구현합니다. 이 메서드들은 AbstractClass에서 정의된 메서드들과 같은 이름을 사용하지만, 다른 구현을 제공합니다.

마지막으로, clientCode() 함수에서 ConcreteClass의 인스턴스를 만들고 templateMethod()를 호출합니다. 이를 통해 ConcreteClass에서 구현된 primitiveOperation1()primitiveOperation2() 메서드가 실행됩니다.

9. Iterator Pattern

기본 표현을 노출하지 않고 집합 객체의 요소에 순차적으로 액세스할 수 있는 방법을 제공하는 설계 패턴입니다.

Iterator pattern은 컬렉션 내부의 요소를 순차적으로 접근하기 위한 디자인 패턴입니다. 이 패턴은 객체 간의 결합도를 낮추고, 코드의 재사용성을 높이는 데에 유용합니다.

Iterator 패턴의 핵심은, 컬렉션 객체가 요소들을 감추고, 각 요소에 접근할 수 있는 인터페이스를 제공하는 것입니다. 이러한 인터페이스를 통해, 컬렉션 객체는 자신의 내부 구조를 외부로부터 숨길 수 있고, 외부 객체들은 컬렉션 객체의 내부 구조를 알 필요 없이 요소들을 순차적으로 접근할 수 있게 됩니다.

JavaScript에서 Iterator 패턴을 구현하기 위해서는, Symbol.iterator 메서드를 이용해 컬렉션 객체에서 Iterator 객체를 반환하는 방법이 일반적으로 사용됩니다. Iterator 객체는 next() 메서드를 가지고 있으며, 이를 호출할 때마다 컬렉션의 다음 요소를 반환합니다. 예를 들어, 배열의 Iterator 객체를 생성하고, for...of 문을 사용해 배열의 요소들을 순차적으로 출력하는 코드는 아래와 같습니다.

const arr = [1, 2, 3];

const iterator = arr[Symbol.iterator]();

for (const item of arr) {
  console.log(item);
}

Iterator 패턴을 사용하면, 컬렉션 객체의 내부 구조를 알 필요 없이 각 요소들을 순차적으로 접근할 수 있으므로, 코드의 유연성과 재사용성을 높일 수 있습니다.

10. Adaptor Pattern

기존 클래스의 인터페이스를 다른 인터페이스로 사용할 수 있도록 하는 디자인 패턴입니다.

Adapter Pattern은 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환하는 패턴입니다. 이 패턴은 기존 코드를 수정하지 않고, 호환되지 않는 인터페이스를 가진 클래스들을 함께 동작할 수 있도록 해줍니다.

JavaScript 예제를 통해 이해해보겠습니다.

// 우리가 사용하고자 하는 interface
class TargetInterface {
  request() {
    return "TargetInterface: Handling the request.\n";
  }
}

// 사용하고자 하는 클래스
class Adaptee {
  specificRequest() {
    return ".eetpadA eht fo roivaheb laicepS\n";
  }
}

// Adapter class
class Adapter extends TargetInterface {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }

  request() {
    const result = this.adaptee.specificRequest().split("").reverse().join("");
    return `Adapter: (Translated) ${result}`;
  }
}

// 사용
function clientCode(target) {
  console.log(target.request());
}

const adaptee = new Adaptee();
console.log("Adaptee: " + adaptee.specificRequest());

const adapter = new Adapter(adaptee);
clientCode(adapter);

위 코드에서 TargetInterface는 우리가 사용하고자 하는 인터페이스입니다.

Adaptee는 우리가 실제로 사용하고자 하는 클래스이지만, 인터페이스가 TargetInterface와 호환되지 않습니다.

이때 Adapter 클래스가 필요합니다. Adapter 클래스는 Adaptee 클래스의 인스턴스를 받아들이고, TargetInterface 인터페이스를 구현하여 Adaptee 클래스의 인스턴스와 함께 사용할 수 있게 합니다.

clientCode 함수는 TargetInterface를 인자로 받아들여 TargetInterface를 사용하는 코드입니다.

실행 결과는 다음과 같습니다.

Adaptee: .eetpadA eht fo roivaheb laicepS
Adapter: (Translated) Special behavior of Adaptee.

AdapteespecificRequest() 메서드가 반환하는 문자열은 뒤집혀 있습니다.
Adapter 클래스는 specificRequest() 메서드를 호출하여 문자열을 받아들이고, 그 문자열을 뒤집어서 새로운 문자열을 반환합니다.

이를 통해 Adaptee 클래스의 인스턴스를 TargetInterface와 함께 사용할 수 있게 되었습니다.

11. Facade Pattern

클래스 라이브러리와 같은 더 큰 코드 본문에 대한 단순화된 인터페이스를 제공하는 디자인 패턴입니다.

Facade pattern은 소프트웨어 공학에서 구조 패턴(structural pattern) 중 하나로, 복잡한 서브시스템(subsystem)을 쉽게 사용할 수 있도록 간단한 인터페이스를 제공하는 패턴입니다. Facade는 건물의 정면이나 정면의 일부를 가리키는데, 이 패턴의 목적도 마찬가지로 복잡한 시스템의 정면에 간략한 인터페이스를 제공하는 것입니다.

간단한 예시를 통해 Facade pattern을 이해해보겠습니다.

예를 들어, 어떤 프로그램에서 파일을 압축하는 기능을 구현해야 한다고 가정해봅시다. 이때 파일 압축을 위해서는 여러 개의 클래스와 인터페이스를 사용해야 합니다.

파일을 읽고 쓰는 기능을 구현한 클래스, 압축 알고리즘을 구현한 클래스, 압축된 파일을 저장하는 클래스 등이 모두 필요합니다.

이런 경우, Facade pattern을 사용하면 이 복잡한 시스템을 쉽게 사용할 수 있습니다.

아래는 JavaScript를 사용하여 Facade pattern을 구현한 예시입니다.

class FileReader {
  read(file) {
    console.log(`Reading ${file}`);
  }
}

class Compressor {
  compress(file) {
    console.log(`Compressing ${file}`);
  }
}

class FileWriter {
  write(file) {
    console.log(`Writing ${file}`);
  }
}

class ZipFileFacade {
  constructor() {
    this.reader = new FileReader();
    this.compressor = new Compressor();
    this.writer = new FileWriter();
  }

  zip(file) {
    this.reader.read(file);
    this.compressor.compress(file);
    this.writer.write(`${file}.zip`);
    console.log(`File ${file} has been compressed successfully!`);
  }
}

const zipFacade = new ZipFileFacade();
zipFacade.zip('example.txt');

위 예시에서는 FileReader, Compressor, FileWriter 클래스가 서브시스템을 구성하고 있습니다.

그리고 ZipFileFacade 클래스가 이 서브시스템에 간단한 인터페이스를 제공합니다. ZipFileFacade 클래스의 zip 메서드를 호출하면, 이 메서드 내에서 FileReader, Compressor, FileWriter 클래스를 사용하여 파일을 읽고 압축하고 쓰기 작업을 수행합니다.

이렇게 Facade 패턴을 사용하면, 복잡한 서브시스템을 쉽게 사용할 수 있습니다.

0개의 댓글