💡 간단 설명:
구체적인 클래스 이름을 직접 명시하지 않고도 객체를 생성할 수 있도록 하는 방법
즉, 객체 생성 로직을 별도의 팩토리 클래스로 분리하여, 클라이언트 코드에서는 어떤 구체적인 객체가 생성되는지 알 필요 없이 인터페이스나 추상 클래스를 통해 객체를 사용할 수 있게 해줌
팩토리 패턴이란, 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴이다.
1️⃣ 객체 생성의 책임 분리
객체를 생성하는 코드를 팩토리 클래스에 모아두어, 객체 생성 방식이 변경되더라도 클라이언트 코드에 영향을 주지 않는다.
2️⃣ 코드의 유연성과 확장성 향상
새로운 종류의 객체가 추가되더라도 팩토리 클래스를 수정하거나 확장하면 되므로, 클라이언트 코드는 변경 없이 그대로 유지할 수 있다.
3️⃣ 의존성 감소
클라이언트는 구체적인 클래스에 의존하지 않고 추상적인 인터페이스나 추상 클래스를 사용하므로, 코드 결합도가 낮아진다.
자바스크립트에서 구현한다면 간단하게 new Object()를 이용할 수 있다.
const num = new Object(42)
const str = new Object('abc')
num.constructor.name; // Number
str.constructor.name; // String
숫자를 전달하거나 문자열을 전달함에 따라 다른 타입의 객체를 생성한다.
즉, 전달받은 값에 따라 다른 객체를 생성하고 인스턴스의 타입 등을 정하는 것이다.
커피 팩토리를 기반으로 라떼를 생상하는 코드를 작성해보았다.
class CoffeeFactory {
static createCoffee(type) {
const factory = factoryList[type]
return factory.createCoffee()
}
}
class Latte {
constructor() {
this.name = "latte"
}
}
class Espresso {
constructor() {
this.name = "Espresso"
}
}
class LatteFactory extends CoffeeFactory{
static createCoffee() {
return new Latte()
}
}
class EspressoFactory extends CoffeeFactory{
static createCoffee() {
return new Espresso()
}
}
const factoryList = { LatteFactory, EspressoFactory }
const main = () => {
// 라떼 커피를 주문한다.
const coffee = CoffeeFactory.createCoffee("LatteFactory")
// 커피 이름을 부른다.
console.log(coffee.name) // latte
}
main()
CoffeeFacotry라는 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스인 LatteFactory가 구체적인 내용을 결정한다.
위 코드는 의존성 주입이라고 볼 수 있다.
자바에서는 주로 추상 클래스나 인터페이스를 이용해 팩토리 패턴을 구현한다. 아래 예제는 커피 팩토리 패턴을 이용해 커피 객체를 생성하는 과정을 보여준다.
enum CoffeeType {
LATTE,
ESPRESSO
}
abstract class Coffee {
protected String name;
public String getName() {
return name;
}
}
class Latte extends Coffee {
public Latte() {
name = "latte";
}
}
class Espresso extends Coffee {
public Espresso() {
name = "Espresso";
}
}
class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch (type) {
case LATTE:
return new Latte();
case ESPRESSO:
return new Espresso();
default:
throw new IllegalArgumentException("Invalid coffee type: " + type);
}
}
}
public class Main {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
System.out.println(coffee.getName()); // latte
}
}
CoffeeFacotry 밑에 Coffee 클래스르 놓고 해당 클래스를 상속하는 Latte, Espresso 클래스를 기반으로 구현한 모습이다.
코드를 보충 설명해보면 다음과 같다.
1. 추상화된 객체 생성: Coffee라는 추상 클래스를 통해 공통 인터페이스를 정의하고, 이를 상속받은 Latte와 Espresso 클래스에서 구체적인 객체를 구현한다.
2. 스위치 문을 통한 분기 처리: CoffeeFactory 클래스의 createCoffee 메서드는 전달된 CoffeeType에 따라 적절한 커피 객체를 생성한다.
3. 확장성: 새로운 커피 종류가 추가될 경우, Coffee를 상속하는 새로운 클래스를 만들고, CoffeeFactory에 해당 타입에 대한 분기만 추가하면 되므로 클라이언트 코드는 수정할 필요가 없게 된다.
** 모든 코드는 https://github.com/wnghdcjfe/csnote/tree/main/ch1 에서 참고하였습니다.
열공 머쪄요