본문 바로가기
Design Pattern/Creational Design

[Design Pattern] (Creational) 팩토리 패턴(Factory Pattern)

by song.ift 2023. 3. 26.

디자인 패턴 유형

  • Creation Design Pattern
    • 객체의 생성에 관련된 패턴
    • 객체의 생성 로직을 숨김으로써 유연성을 향상시킨다.
  • Strutural Design Pattern
    • 클래스와 객체의 구성에 관련된 패턴
  • Behavioral Design Pattern
    • 객체와 객체 간의 통신에 관련된 패턴

 

[Creational Design Pattern] 팩토리 패턴(Factory Pattern)

팩토리 패턴은 비슷한 객체를 공장에서 찍어내듯이 반복적으로 생성할 수 있게 하는 패턴이다. 객체를 생산하는 공장(Factory)을 구현하는 방법이라고 생각하면 된다.

개발자가 컴파일 시점에서 어떤 객체(구체적인 타입, 클래스)를 생성해야될지 모르고, 런타임 환경에서 동적으로 객체를 생성해야 할 때도 사용한다. 팩토리 패턴의 가장 흔한 사례는 Object()를 이용한 객체 생성시, 주어지는 값의 타입에 따라 String, Boolean, Number 등으로 객체가 생성되는 것이다.

팩토리 패턴은 크게 두 가지로 설명할 수 있다.

  1. 객체를 사용하는 코드에서 객체의 생성을 떼어내서 별도의 객체에 추상화 한다.
  2. 상속관계의 경우, 상위 객체가 중요한 뼈대를 담당하고, 하위 객체에서 객체 생성에 관한 구체적인 내용을 결정한다.

각각의 레시피(우유, 아메리카노 등) 들어있는 하위 클래스가 컨베이어 벨트를 통해 전달되고, 상위 클래스인 바리스타 공장에서 이 레피시들을 토대로 우유 등등을 생산하는 생산 공정을 생각하면 된다.

 

 장점

  • 상위 클래스와 하위 클래스가 분리되어 느슨한 결합을 가지지만 상위 클래스에서는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 많은 유연성을 갖게 된다.
  • 객체 생성 로직이 따로 떼어져 있기 때문에 코드 리팩토링 하더라도 한 곳만 고칠 수 있게 되니 유지 보수성이 증가한다.

 

ex)

아래와 같이 자동차 객체를 반복해서 생성해야된다고 하면,

const car1 = {
    name: "아반떼",
    price: "1,570 ~ 2,453만원",
    getInfo: function(){
    	return this.name+"의 가격은 "+this.price+" 입니다.";
    }
}
const car2 = {
    name: "쏘나타",
    price: "2,386 ~ 3,367만원",
    getInfo: function(){
    	return this.name+"의 가격은 "+this.price+" 입니다.";
    }
}

위 코드의 단점은 계속해서 코드가 반복되고, car 객체의 수정사항이 생기면 모든 car 객체를 찾아 수정해주어야 한다. 이 단점을 없애기 위해 car 객체를 만들어 주는 factory 함수를 만들 수 있다.

const factory = function(param){
    return {
        name: param.name,
        price: param.price,
        getInfo: function(){
            return this.name+"의 가격은 "+this.price+" 입니다.";
        }
    }
}
const car1 = factory({name: "아반떼", price: "1,570 ~ 2,453만원"});
const car2 = factory({name: "쏘나타", price: "2,386 ~ 3,367만원"});
console.log(car1, car2);

factory 라는 함수에서 파라미터로 받은 정보로 새로운 Object를 리턴해 준다. 이제 car 객체의 수정이 발생하게되면 factory 함수만 수정하면 된다.

 

하지만 여기에도 단점은 있다. console의 결과를 보면,

중복되는 코드도 줄었고, 유지보수의 편의성도 챙겼다. 하지만 같은 기능을 하는 getInfo라는 메소드가 객체를 생성 할 때 마다 추가되게된다. 그래서 Prototype의 메소드 설정으로 중복 메서드의 추가 문제는 해결할 수 있다.

 

이제 Class 키워드를 활용하여 위의 코드를 Factory pattern으로 작성.

class Car{
    constructor(info){
        this.name = info.name;
        this.price = info.price;
    }
    getInfo(){
        return this.name+"의 가격은 "+this.price+" 입니다.";
    }
    static factory(name){
        if(name === "Avante"){
           return new Avante();
        }else if(name === "Sonata"){
           return new Sonata();
        }
    }
}
class Sonata extends Car{
    constructor(){
        super({name: "쏘나타", price: "2,386 ~ 3,367만원"});
    }
}
class Avante extends Car{
    constructor(){
        super({name: "아반테", price: "1,570 ~ 2,453만원"});
    }
}
const avante = Car.factory("Avante");
const sonata = Car.factory("Sonata");
console.log(avante, sonata);

Car class 에는 공통으로 사용되는 getInfo 메소드와 파라미터를 받아 해당하는 객체를 리턴해주는 factory 메소드가 있다. 

Factory Pattern 에서는 객체를 동적으로 생성하는 메소드를 static으로 정의하여 Car를 new로 생성하지 않아도 사용할 수 있게 한다. Car를 상속받는 Sonata, Avante Class를 생성한다.

 

console의 결과

각 Avante, Sonata 객체별로 정보를 가지고 있고, 상속받은 부모객체인 Car의 메소드를 공유할 수 있다. 개발자는 어떤 차종의 Class를 사용할지 빌드 단계에서 결정하지 않아도 되며, 런타임 환경에서 원하는 차종의 Class를 동적으로 생성할 수 있다. (Factory의 파라미터로 전달)

 

댓글