팩토리 패턴

차분한열정·2022년 1월 2일

디자인 패턴

목록 보기
2/15

1. 심플 팩토리 패턴

public class Start {
	public static void main(Strings[] args) {
    	Printer printer1 = PrinterFactory.createPrinter('General');
        Printer printer2 = PrinterFactory.createPrinter('Special');
		output(printer1);
        output(printer2);
    }
    
    public void output(Printer printer) {
    	printer.print();
    }
}


public class PrinterFactory {
	public static Printer createPrinter(String type) {
    	
        Printer printer;
    
    	if(type.equals("General")) {
        	...
            ...
            return instance;
        } else if (type.equals("Special")) {
        	...
        	...
            return instance;
        } else {
        	...
            return instace;
        }
    }
    
}

팩토리 패턴의 가장 간단한 구현은 이렇게 메인 로직에서 사용할 특정 객체를 팩토리 클래스를 사용해서 생성해서 사용하는 것이다. 하지만 이렇게 되면 만들어야할 객체의 종류가 늘어날수록 팩토리 클래스 안의 로직이 복잡해진다는 단점이 있다.

2. 팩토리 메소드 패턴

public class Start {
	public static void main(Strings[] args) {
    	output(new GeneralPrinterFactory());
    }
    
    public void output(PrinterFactory printerFactory) {
    	Printer printer = printerFactory.createPrinter();
        printer.print();
    }
}

public interface PrinterFactory {
	default Printer createPrinter() {
    	doPhase1();
        doPhase2();
    }
    
    void doPhase1(); 
    void doPhase2();
}

public class GeneralPrinterFactory extends PrinterFactory {
	public void doPhase1() {
    	...
    }
    
    public void doPhase2() {
    	...
    }
}

public class SpecialPrinterFactory extends PrinterFactory {
	public void doPhase1() {
    	...
    }
    
    public void doPhase2() {
    	...
    }
}

이런 식으로 특정 종류(들)의 인스턴스 생성 역할을 하나의 특정 팩토리 객체에 맡기는 패턴, 지금 GeneralPrinterFactory와 SpecialPrinterFactory가 상속하는 인터페이스나 추상 클래스(ex. PrinterFactory 인터페이스)에는 어떤 프린트든 생성에 필요한 최소한의 코드를 두고 각 팩토리에 각 프린터 타입에 맞는 코드를 추가하면 된다. 이런 패턴으로 구현을 하면 위의 심플 팩토리 패턴에서는 PrinterFactory 클래스는 프린터 타입이 추가될 때마다 코드가 변경되어야 하지만, 이 팩토리 메소드 패턴에서는 PrinterFactory 인터페이스의 추상 메소드나 구체 메소드가 변경될 필요가 없다. 물론 이렇게 하면 Open-closed 규칙은 지킬 수 있겠지만 클래스의 수가 늘어난다 ㅜ 여기서 createPrinter 처럼 하나의 특정 객체를 리턴하는 팩토리 메소드가 존재하기 때문에 팩토리 메소드 패턴이라고 하는 것 같다(추정)

3. 추상 팩토리 패턴

주로 특정 객체가 의존하는 또다른 객체들을 한번에 생성할 때 추상 팩토리 패턴을 적용한다. 만약 이제 프린트의 종류뿐만 아니라 무료/유료 회원인지에 따라 다른 설정을 적용하는 스펙이 추가되었다고 해보자. 그리고 유 무료 여부에 따라 프린터에서 사용하는 각종 잉크의 종류, 종이의 종류, 인쇄 속도 등이 달라지게 되는데, 일단은

public class Start {
	public static void main(Strings[] args) {
    	output(new GeneralPrinterFactory(), 'free');
        output(new SpecialPrinterFactory(), 'paid');
    }
    
    public void output(PrinterFactory printerFactory, String userType) {
    	Printer printer = printerFactory.createPrinter(userType);
        printer.print();
    }
}

이런 식으로 대응하면 될 것이다. 그리고 GeneralPrinterFactory 클래스와 SpecialPrinterFactory가 내부적으로 생성하는 객체들을 별도로 생성하기 위해 안에서 예를 들어 각종 createInk, createPaper 같은 메소드에서 if-else 분기문이 생겨날 것이다. 왜냐하면 프린터는 다양한 객체에 의존할 것이기 때문인데 이렇게 하면 나중에 유/무료 회원 뿐만 아니라 또 다른 종류의 회원 타입이 생기게 되면 각 createInk, createPaper 같은 메소드들의 내부를 또 수정해줘야 한다.

이때 이러한 객체들을 한번에 생성하는 별도의 하위 팩토리를 만들고 그 하위 팩토리 객체를 PinrterFactory 클래스 안에 주입함으로써 그렇게 재료가 되는 객체들을 한번에 그 팩토리 객체로 생성한다면 훨씬 코드가 간결해진다. 즉 미리 Printer가 의존하는 모든 객체들에 대한 별도의 팩토리를 또 만들어서 활용하는 것이다.

public class Start {
	public static void main(Strings[] args) {
    	output(new GeneralPrinterFactory(new FreeConsumerIngredientFactory()));
        output(new SpecialPrinterFactory(new PaidConsumerIngredientFactory()));
    }
    
    public void output(PrinterFactory printerFactory, InkJet inkjet) {
    	Printer printer = printerFactory.createPrinter();
        printer.print();
    }
}

이렇게 작성하게 되면 또다른 회원 유형이 포함된다고 해도 단지 또 새로운 하위 팩토리 클래스(ex. EventConsumerIngredientFactory)만 추가해주면 된다. 물론 역시 이 방법도 클래스의 수를 늘리게 된다.

사실 팩토리 메소드 패턴과 추상 팩토리 패턴의 차이를 명확하게 느끼기가 힘든데 정확한 차이를 굳이 짚어보자면

1) 팩토리 메소드 패턴은

상속을 사용해서 팩토리 인터페이스(or 추상 클래스)와 팩토리 구체 클래스를 두고 특정 객체 하나에 대한 생성을 팩토리 구체 클래스에 맡기는 방식이라면 즉 뭔가를 생성해내는 것이 구체 메소드인 경우라면 팩토리 메소드 패턴이다.

2) 추상 팩토리 패턴은

특정 구체 클래스 안에서 합성(composition)을 통해 어떤 객체(들)을 생성하는 역할을 특정 팩토리 객체에 맡긴 경우라고 할 수 있다. 즉 여기서 뭔가를 생성해내는 것은 합성된 해당 팩토리 객체이다.

하나의 구체 클래스 관점에서 보면 좀더 이해하기 쉽다. 하나의 구체 클래스가 자신의 구체 메소드를 사용해서 뭔가 객체를 생성하는 역할을 한다면 이것은 팩토리 메소드 패턴이고, 이 하나의 구체 클래스가 다른 구체 클래스를 사용해서 그 클래스에 특정 객체를 생성하는 역할을 맡기는 것이 추상 팩토리 패턴인 것이다. 그런데 그럼 맡김을 당한 구체 클래스 입장에서는 또 그것이 팩토리 메소드 패턴인 것 아니냐? 하는 부분 때문에 이런 인식의 혼란이 생기는 것 같다. 혹시 이 부분 정확히 설명하실 수 있는 분 있으면 댓글을 부탁..!

profile
성장의 기쁨

0개의 댓글