Skip to main content

Simple Factory Pattern

View all Design Patterns

별도의 팩토리 메서드를 활용해 객체를 생성하는 방식이다. 클라이언트는 해당 객체의 구체적인 생성 로직을 알 필요가 없다.

Before

Class Diagram

위의 경우, 객체를 호출하는 쪽이 생성자(e.g. new)에 의존하고 있기 때문에 다음과 같이 생성해야 한다.

Code

interface Animal {
sound(): string;
}

class Duck implements Animal {
sound(): string {
return "Quack";
}
}

class Cat implements Animal {
sound(): string {
return "Meow";
}
}

// when client creates instances
const duck = new Duck();
const cat = new Cat();

console.log(`duck sounds ${duck.sound()}`);
console.log(`cat sounds ${cat.sound()}`);

하지만 이럴 경우, 다음과 같은 문제점이 발생한다.

  • 클라이언트는 DuckCat이라는 특정 클래스를 알고 있어야 한다.
  • 클래스와 객체가 서로 의존하고 있다. (Tight Coupling)
  • 생성자 코드에 변경이 있을 경우, 모든 클라이언트 코드를 변경해야 한다.

Simple Factory Pattern을 사용하면 이러한 로직을 추상화시켜줄 수 있다. Simple Factory Pattern의 최대 장점은 인스턴스 생성 로직을 한번에 관리해줄 수 있다는 점이다.


After

Class Diagram

이렇게 되면, 클라이언트는 AnimalFactory만 바라보게 되면서 DuckCat 클래스에 의존하지 않을 수 있다.

Code

interface Animal {
sound(): string;
}

class Duck implements Animal {
sound(): string {
return "Quack";
}
}

class Cat implements Animal {
sound(): string {
return "Meow";
}
}

enum AnimalType {
DUCK,
CAT,
}

class AnimalFactory {
static create(type: AnimalType): Animal {
switch (type) {
case AnimalType.DUCK:
return new Duck();
case AnimalType.CAT:
return new Cat();
default:
throw new Error("Invalid animal type");
}
}
}

// when client creates instances
const duck = AnimalFactory.create(AnimalType.DUCK);
const cat = AnimalFactory.create(AnimalType.CAT);

console.log(`duck sounds ${duck.sound()}`);
console.log(`cat sounds ${cat.sound()}`);

Pros and Cons

이를 정리하면 다음과 같다.

Pros

  • 공통의 인터페이스로부터 여러개의 클래스를 필요로 할 경우, 인스턴스 생성 로직을 한번에 관리할 수 있다.
  • 생성 로직이 단순하고 자주 변경되지 않을 경우
  • 클라이언트를 구체적인 클래스 구현으로부터 분리시킬 수 있다.
  • 객체를 생성할 때, switch(또는 if)를 활용하기 적절해 보이는 경우
    • Logger에서 info, warn, debug, error를 구분할 때 사용할 수 있다.
    • TextButton, IconButton, TextIconButton과 같이 Button이라는 공통된 기능을 활용할 경우 사용할 수 있다.

Cons

  • SOLID의 Open Closed Principle에 위배된다.
    • 새로운 클래스를 생성하면 팩토리 클래스 또한 수정되어야 한다. (switch 항목 추가 등)
  • 생성자가 복잡할 경우 별로 유용하지 못하다.
  • 여전히 구체적인 클래스의 변경사항이 팩토리 클래스에 영향을 준다. (구체적인 구현 클래스와 팩토리 클래스 간의 강한 결합)
Related Links