본문 바로가기

Domain/디자인패턴

[디자인패턴] 팩토리 패턴(Factory Pattern)

팩토리 패턴(Factory Pattern)

팩토리 패턴은 인스턴스를 만드는 과정을 서브클래스로 위임하는 것이다. 즉, new 키워드를 사용하여 메모리를 할당하는 부분을 서브 클래스에서 작성하게된다. 이를 통해 객체 생성을 캡슐화하고 구상 클래스(concrete class)에 대한 의존성이 줄어든다는 이점이 있다.

구상(구체적인 형상) 클래스는 실제 new를 통해 메모리화 가능한 클래스, 단독으로 메모리에 올라갈 수 없는 추상 클래스와 대립되는 개념

 

팩토리 패턴은 팩토리 메서드 패턴과 추상 팩토리 패턴으로 나뉜다.

 

팩토리 메서드 패턴

조건에 따른 객체를 생성하기 위한 과정을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성해서 해당 객체를 반환한다.

예제. 좋지 않은 코드 예시

우선 Type 추상 클래스를 정의하고 이를 상속받는 TypeA/B/C 클래스를 정의했다.

public abstract class Component {
    abstract String getComponentName();
}

public class Button extends Component{
    @Override
    String getComponentName(){
        System.out.println("버튼");
    }
}

public class Toggle extends Component{
    @Override
    String getComponentName(){
        System.out.println("토글");
    }
}

public class Switch extends Component{
    @Override
    String getComponentName(){
        System.out.println("스위치");
    }
}

 

그리고 ClassA의 createType() 메서드에서 타입을 받아 switch문을 통해 객체를 생성한다.

public class Console {
    Component cmp1, cmp2, cmp3;

    void createComponent(){
    	cmp1 = new Button()
        cmp2 = new Toggle()
        cmp3 = new Switch()
    }
}

위를 보면 동작에는 전혀 이상이 없지만, 만약에 다른 클래스에서도 객체를 생성하는 코드가 필요하다면 어떻게 될까?

위의 컴포넌트를 만드는 코드는 ClassB, ClassC에서도 필요할 것이다. 만약, 컴포넌트를 생성할 때 매개변수도 넘겨야 되게 수정된다면 이와 연관된 코드를 전부 수정하는데 꽤 오랜 시간이 걸릴 것이다.

 

 

예제. 개선된 코드 예시

먼저  객체를 생성하는 추상 메서드가 정의된 추상 클래스를 정의한다. (기본적으로 Type 관련된 클래스는 기존과 같다.)

abstract class Factory{
    //Factory Method
    fun getComponent(usage:Usage):Component
}

class ComponentFactory : Factory{
    fun getComponent(usage:Usage):Component{
    	when(usage){
            Usage.BUTTON -> return Button()
            Usage.TOGGLE -> return Toggle()
            Usage.SWITCH -> return Switch()
            default -> return Component()
        }
    }
}

 

 

실제 팩토리 메서드 패턴을 활용한다.

public class Test {
    public static void main(String[] args){
        ComponentFactory mtFactory = ComponentFactory();
        
        Component cmp1 = mtFactory.getComponent(Usage.BUTTON)
        Component cmp2 = mtFactory.getComponent(Usage.TOGGLE)
        
    }
}

위 과정을 통해 여러 타입에 대한 생성 과정이 코드 이곳 저곳에서 필요할 때 코드의 반복 없이 간단하게 팩토리 메서드 패턴을 활용하여 객체를 생성할 수 있다. 그리고, 실제 활용되는 부분에서는 Usage enum에 대해서만 파악하고 있다면 마음대로 객체를 생성할 수 있다.

 


추상 팩토리 패턴

서로 관련이 있는 객체를 그룹화하여 팩토리 클래스로 만들고, 이들을 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴이다. 팩토리 메서드 패턴을 조금 더 캡슐화한 방식이다.

 

Button과 Toggle이 있는 GUI 프로그래밍을 Window, Mac에 따라 생성한다고 가정하자. 이 경우 OS 별로 아이템을 그룹화할 필요가 있다.

 

먼저 기본적인 레이아웃인 Button과 Toggle을 정의해준다.

interface Button{
    fun paint();
}

interface Toggle{
    fun switch();
}

class WinButton:Button{
    override fun paint(){}
}

class MacButton:Button{
    override fun switch(){}
}

class WinToggle:Toggle{
    override fun paint(){}
}

class MacToggle:Toggle{
    override fun switch(){}
}

 

이후 각 운영체제에 맞는 클래스를 생성해주고 해당 클래스 내부에선 운영체제에 적합한 레이아웃을 생성하도록 한다. 이 과정이 추상 팩토리 패턴을 활용하는 과정이다. 버튼, 토글이 운영체제에 맞게 그룹화되었다.

interface GUIFactory
{
    fun CreateButton() : Button;
    fun CreateToggle() : Toggle;
}

class WinFactory : GUIFactory{
    fun CreateButton()= WinButton()
    fun CreateToggle()= WinToggle()
}

class MacFactory : GUIFactory{
    fun CreateButton()= MacButton()
    fun CreateToggle()= MacToggle()
}

 

최종적으로 운영체제에 따른 패토리 메서드 패턴을 적용하여 구현한다. 앞서 설명한 듯 추상 팩토리 패턴에는 팩토리 메서드 패턴이 활용될 수 있다.

public class FactoryOfGUIFactory {
    public GUIFactory createGUI(type: String) : GUIFactory{
        val guiFactory : GUIFactory = null
        
        when (type) {
            "Window" -> guiFactory = WinFactory()
            "Mac" -> MacFactory()
        }
        
        guiFactory.createKeyboard()
        guiFactory.createMouse()
        
        return guiFactory
    }
}

 

마지막으로 이를 실제 클라이언트에서 사용하게 된다.

public class Client {
    public static void main(String args[]){
        FactoryOfGUIFactory factoryOfGuiFactory = new FactoryOfGUIFactory();
        GUIFactory gui = factoryOfGuiFactory.createGUI("Window");
    }
}

 

 

 

요약

  • 팩토리 패턴: 인스턴스화 과정을 팩토리 클래스로 위임하는 것
  • 팩토리 메서드 패턴: 조건에 따른 객체화 과정을 팩토리 클래스로 위임하는 것
  • 추상 팩토리 패턴: 조건에 따른 객체화 과정에서 서로 연관된 객체를 팩토리 클래스 묶어서 인스턴스화하는 것