Factory Method (팩토리 메서드)

JS (TIL & Remind)·2022년 2월 15일
1

Factory Method (팩토리 메서드)

Factory Method 패턴 이란?

객체를 만들기 위한 인터페이스를 정의하나, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 하위 클래스가 정하도록 하는 패턴. 즉, 객체 생성 처리를 서브클래스로 분리 해 처리하도록 캡슐화 하는 패턴.

Factory Method 패턴의 구조

  • Creator
    • Product 타입의 객체를 반환하는 FactoryMethod를 선언한다.
    • 기본적인 FactoryMethod를 구현하고, ConcreteProduct를 반환한다.
    • AnOperation - Product 객체 생성을 위해 FactoryMethod를 호출한다.
  • ConcreteCreator - FactoryMethod를 재정의 하여 ConcreteProduct 인스턴스를 반환한다.
  • ConcreteProduct - Product에 정의된 인터페이스를 실제로 구현한다.
  • Product - FactoryMethod가 생성하는 객체의 인터페이스를 정의한다.

Factory Method 패턴을 사용하는 이유

  • 클래스의 생성과 사용의 처리 로직을 분리하여 결합도를 낮추기 위함.
  • 객체 생성 처리를 서브클래스로 분리 함으로써, 직접 객체를 사용하는 것을 방지하며 이는 효율적인 코드 제어를 할 수 있고 의존성을 제거할 수 있다.

Factory Method 패턴의 장점과 단점

장점

  • 객체의 자료형이 서브 클래스에 의해서 결정되므로 확장에 용이하다.
    • 서브 클래스에서 객체를 생성할 때, 상위 클래스에서 생성되는 객체에 대한 정확한 타입을 몰라도 되기 때문.
  • DIP (Dependency Inversion Principle, 의존 관계 역전의 원칙)를 성립한다.

    DIP (Dependency Inversion Principle, 의존 관계 역전의 원칙) 란?
    객체 지향 설계의 5대 원칙(SOLID) 중 하나로써,
    의존 관계를 맺을 때, 변화하기 쉬운것 보단 변화하기 어려운 것에 의존해야 한다는 원칙.
    변화하기 어려운 것 - 정책, 전력과 같은 어떤 큰 흐름이나 개념처럼 추상적인 것.
    변화하기 쉬운 것 - 구체적인 방식, 사물 등과 같은 것.
    으로 생각하면 이해하기 조금 더 수월하다.

    참고 - https://defacto-standard.tistory.com/113

단점

  • 새로 생성할 객체의 종류가 늘어날 때 마다 서브 클래스 재정의로 인한 불필요한 클래스들의 생성 가능성이 있다.

예제 코드

구현 예제는 스타크래프트의 팩토리에서 유닛을 뽑기 위해 필요한 자원 객체를 Factory Method를 이용해 만든 예제 입니다.
Typescript로 abstract class(추상 클래스)와 abstract method(추상 메서드)를 이용해서 구현했습니다.

💡 Javascript는 프로토타입 언어이기 때문에, 기본적으로 클래스라는 개념을 제공하지 않는다. ES5 이후에 나온 class는 문법적으로만 지원하고, 엄밀히 말하면 프로토타입 기반으로 클래스를 흉내낸 함수 이다. 또한, Javascript에서는 abstract (추상화)를 직접 구현해야 한다.
// Creator - 기본적인 FactoryMethod를 구현하고, ConcreteProduct를 반환한다.
abstract class Factory {
  
		// Product 타입의 객체를 반환하는 FactoryMethod를 선언한다.
    public abstract createUnit(): Unit;

    // Product 객체 생성을 위해 FactoryMethod를 호출한다.
    public getUnit(): Unit {
				return this.createUnit();
    }
}

// ConcreteCreator - FactoryMethod를 재정의 하여 ConcreteProduct 인스턴스를 반환한다.
// 추상 클래스를 상속하는 클래스에선 추상 메서드를 꼭 구현해야 한다.
class CreateSiegeTank extends Factory {
    public createUnit(): Unit {
				console.log('SiegeTank를 만들었습니다.');
        return new SiegeTank();
    }
}

class CreateGoliath extends Factory {
    public createUnit(): Unit {
				console.log('Goliath을 만들었습니다.');		
        return new Goliath();
    }
}

// Product - FactoryMethod가 생성하는 객체의 인터페이스를 정의한다.
interface Unit {
		mineral: number;
		gas: number;
		food: number;
}

// ConcreteProduct - Product에 정의된 인터페이스를 실제로 구현한다.
class SiegeTank implements Unit {
    mineral: number = 0;
		gas: number = 0;
		food: number = 0;

		constructor() {
				this.mineral = 150;
				this.gas = 100;
				this.food = 2;
		}
}

class Goliath implements Unit {
    mineral: number = 0;
		gas: number = 0;
		food: number = 0;

		constructor() {
				this.mineral = 100;
				this.gas = 50;
				this.food = 2;
		}
}

// Client Code
function callUnit(factory: Factory): void {
		const unitInfo = factory.getUnit();
		console.log(`
				미네랄 : ${unitInfo.mineral}
				가스 : ${unitInfo.gas}
				인구수 : ${unitInfo.food}
				를 소모 하였습니다.
		`);
}

callUnit(new CreateSiegeTank());
callUnit(new CreateGoliath());

// 결과
// SiegeTank를 만들었습니다.
// 미네랄 : 150
// 가스 : 100
// 인구수 : 2
// 를 소모 하였습니다.
// Goliath을 만들었습니다.
// 미네랄 : 100
// 가스 : 50
// 인구수 : 2
// 를 소모 하였습니다.
profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)

0개의 댓글