[Creational Patterns] - Factory Method

Lee Jeong Min·2022년 1월 1일
0

디자인 패턴

목록 보기
2/23
post-thumbnail

이 글은 refactoring guru의 - Factory Method 부분을 읽고 번역 및 정리한 글입니다.

Creational Design Patterns[디자인 패턴] - 디자인 패턴 소개 부분에서 이야기 했듯이, 다양한 객체 생성 메커니즘을 제공하여 기존 코드의 유연성과 재사용성을 증가시킨다.

Factory Method의 도입부분에서 이를 다른 명칭으로 가상 생성자라고 부른다.

의도

Factory Method는 슈퍼클래스에서 객체를 만들기 위한 인터페이스를 제공하면서도, 하위 클래스가 생성될 객체의 유형을 변경할 수 있는 creational design pattern이다.

좀 더 이야기하면 Factory Method는 직접 생성자 호출(constructor) 대신 객체를 생성하는데 사용되는 메서드를 정의한다. 하위 클래스는 이 메서드를 재정의하여 생성될 객체의 클래스를 변경할 수 있다.

문제

이에 대해 자세하게 설명하기 위해, 물류 관리 어플리케이션을 만든다고 가정하자. 앱을 처음 설계할 때 트럭 운송 전용으로 만들었다면 코드의 대부분은 Truck Class와 관련된 부분이 많을 것이다.

얼마 후, 이 앱이 인기가 많아져서 해상 운송 회사로부터 해상 물류를 앱에 포함시켜달라는 요청을 받았다고 해보자.

이 순간, 코드가 이미 기존 클래스에 커플링되어 있다면 프로그램에 새 클래스를 추가하는 것은 간단하지 않을 것이다.
결합도가 높아진 코드들을 분리하고 새로운 클래스를 추가해야하기 때문

앱에 선박을 추가하려면 전체 코드베이스를 변경해야한다. 게다가, 나중에 다른 운송 수단을 앱에 추가하기로 결정한다면, 이 모든것들을 다시 또 해야하는 상황을 맞닥뜨리게 될 것이다.

결과적으로, 운송 객체의 등급에 따라 앱의 동작을 전환하는 조건들로 가득 찬 더러운 코드를 갖게된다.

해결책

Factory Method 패턴은 new 연산자를 사용하여 직접 객체 생성을 호출하는 방법 보다 특수 Factory Method 호출 방법으로 바꿀 것을 제안한다. 객체는 여전히 새 연산자를 통해 생성되지만 Factory Method 내에서 호출되고 있다. Factory Method에 의해 반환되는 객체는 종종 제품이라고 불린다.

하위 클래스는 Factory Method에 의해 반환되는 객체의 클래스를 변경 할 수 있다.

언뜻 보면 이 변경은 무의미해 보일 수 있다. 생성자 호출을 프로그램의 한 부분에서 다른 부분으로 옮겼을 뿐이기 때문이다. 그러나 이제 하위 클래스에서 공장 메서드를 재정의하고 메서드에 의해 생성되는 제품 클래스를 변경할 수 있다.

또한 약간의 제한이 있는데 하위 클래스는 이러한 제품들이 공통 기본 클래스나 인터페이스를 가지고 있는 경우에만 다른 유형의 제품을 반환할 수 있다. 또한 기본 클래스의 Factory Method는 반환 형식을 이 인터페이스로 선언해야 한다.

모든 제품은 동일한 인터페이스를 따라야 한다.

예를 들어 Truck ClassShip Class는 모두 Transport Interface를 구현해야 하며, 이 인터페이스는 배달이라는 메서드를 선언한다. 트럭은 육로로 화물을 운송하고 배는 해상으로 화물을 운송하기 때문에 RoadLogistics 클래스의 Factory Method는 트럭 객체를 반환하고 SeaLogistics 클래스의 Factory Method는 선박을 반환한다.

모든 product class들이 공통 인터페이스를 구현하는 한, 해당 객체를 클라이언트 코드로 전달 가능

Factory Method를 사용하는 코드(클라이언트 코드라고도 함)는 다양한 하위 클래스에서 반환되는 실제 제품 간의 차이를 인식하지 못한다. 고객은 코든 제품을 추상 운송으로 취급하며 모든 운송 객체는 배송 방법이 있어야 한다는 것을 알고 있지만, 정확히 어떻게 작동하는지는 고객에게 중요하지 않다.

구조

  1. 제품은 인터페이스를 선언하며 이는 creator와 하위 클래스 가 생성할 수 있는 모든 객체에 공통부분이다.

  2. Concrete 제품은 제품 인터페이스의 다른 구현체이다.

  3. Creator 클래스는 새 제품 객체를 반환하는 Factory Method를 선언한다. 이 방법의 반환 유형이 제품 인터페이스와 일치하는지 여부가 중요하다.

Factory method를 추상 메서드로 선언하여 모든 하위 클래스가 자체 버전의 메서드로 구현하도록 할 수 있다. 또는 기본 Factory method가 일부 기본 제품 유형을 반환할 수 있다.

보통 Creator Class는 제품과 관련된 핵심 비즈니스 논리가 이미 존재한다. Factory method는 이 논리를 구체적인 제품 클래스에서 분리하는 데 도움이 된다. 예시로 소프트웨어 개발 회사는 프로그래머를 위한 교육 부서를 가질 수 있지만, 회사 전체의 주된 기능은 여전히 프로그래머를 교육시켜 생산하는 것이 아닌 코드를 작성하는 것이다.

  1. Concrete Creators는 기본 Factory Method를 재정의하여 다른 유형의 제품을 반환한다.

Factory Method는 항상 새로운 인스턴스를 생성할 필요는 없다. 또한 캐시, 객체 풀 또는 다른 소스에서 기존 객체를 반환할 수 있다.

적용 가능성

아래는 이 패턴을 적용하기 좋은 상황이다.

  • 코드가 작동해야하는 객체의 정확한 유형과 종속성을 사전에 모를 경우 사용하면 좋다.

  • 라이브러리 또는 프레임워크의 사용자에게 내부 구성요소를 확장하는 방법을 제공하려면 Factory Method를 사용해라.

  • 매번 객체를 재구성하는 대신 기존 객체를 재사용하여 시스템 리소스를 절약하려면 Factory Method를 사용해라.
    → 기존 객체를 재사용(클래스의 생성자를 통해)하거나 새 객체를 생성할 수 있는 규칙적인 방법이기 때문

장단점

장점

  • Creator(생성자함수)와 Concrete(제품, 결과물)간의 긴밀한 결합을 피할 수 있다.

  • 제품 생성 코드를 프로그램의 한 곳으로 이동하여 단일 책임 원칙을 잘 보여줄 수 있다.

  • 열기/닫기 원리. 기존 클라이언트 코드를 위반하지 않고 프로그램에 새로운 유형의 제품을 도입할 수 있다.

단점

  • 패턴을 구현하려면 새로운 하위 클래스를 많이 도입해야해서 코드가 더 복잡해 질 수 있다. 가장 좋은 시나리오는 패턴을 기존 Creator 클래스 계층에 도입하는 경우이다.

Factory Method in TypeScript

TypeScript의 패턴 사용

복잡도: ★☆☆

인기: ★★★

사용 예: Factory Method 패턴은 TS 코드에서 널리 사용된다. 이 기능은 코드에 높은 수준의 유연성을 제공해야할 때 유용하다.

식별: Factory Method는 구체적인 클래스에서 객체를 생성하지만 추상 타입이나 인터페이스의 객체로 반환하는 생성 메서드로 인식할 수 있다.

index.ts

// 이 Creator 클래스는 Product클래스의 객체를 반환하는 Factory Method를 선언
// Creator의 하위 클래스는 일반적으로 이 메서드의 구현을 제공한다.
abstract class Creator {
  // 이 factoryMethod의 일부 기본 구현을 제공할 수도 있다.
  public abstract factoryMethod(): Product;

  // 주된 책임이 제품을 만드는 것이 아닌 제품 객체에 의존하는 몇 가지 핵심 비즈니스 논리 포함
  // 하위 클래스는 공장 메서드를 재정의하고 다른 유형의 제품을 반환함으로써 간접적으로 비즈니스 논리 변경 가능
  public someOperation(): string {
    // 제품 객체를 생성하기 위한 factoryMethod 호출!
    const product = this.factoryMethod();

    return `Creator: The same creator's code has just worked with ${product.operation()}`;
  }
}

// 결과 제품의 유형을 변경하기 위해 factoryMethod 재정의 (overriding 하여)
class ConcreteCreator1 extends Creator {
  public factoryMethod(): Product {
    return new ConcreteProduct1();
  }
}

class ConcreteCreator2 extends Creator {
  public factoryMethod(): Product {
    return new ConcreteProduct2();
  }
}

interface Product {
  operation(): string;
}

class ConcreteProduct1 implements Product {
  public operation(): string {
    return '{Result of the ConcreteProduct1}';
  }
}

class ConcreteProduct2 implements Product {
  public operation(): string {
    return '{Result of the ConcreteProduct2}';
  }
}

JS 간단한 예제

객체 생성을 나머지 코드와 분리한 예제

function Vehicle() {}

function Car() {
  this.say = function () {
    console.log('I am a Car');
  };
}

function Bike() {
  this.say = function () {
    console.log('I am a Bike');
  };
}

function Train() {
  this.say = function () {
    console.log('I am a Train');
  };
}

function VehicleFactory() {
  this.createVehicle = function (vehicleType) {
    let vehicle;
    switch (vehicleType) {
      case 'car':
        vehicle = new Car();
        break;
      case 'Bike':
        vehicle = new Bike();
        break;
      case 'Train':
        vehicle = new Train();
        break;
      default:
        vehicle = new Vehicle();
    }

    return vehicle;
  };
}

const vehicleFactory = new VehicleFactory();

let car = vehicleFactory.createVehicle('car');

car.say();

요약

Creational Design Patterns의 Factory Method Pattern은 생성자와 나머지 코드를 분리하여 가상생성자라고도 불리며 Creator와 제품간의 결합도를 낮춰 새로운 유형의 제품을 쉽게 추가할 수 있도록 만들어준다.

참고 사이트

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글