Factory Pattern

YeJI Kang·2021년 5월 31일
0

Head First Design Pattern

목록 보기
4/14

흔히 말하는 팩토리 패턴(Factory Pattern)에는 팩토리 메소드 패턴(Factory Method Pattern)과 추상 팩토리 패턴(Abstract Factory Pattern) 두 가지 패턴이 있습니다. 이 패턴들에 대해 오늘 순서대로 알아보겠습니다.

팩토리 패턴(Factory Pattern)의 필요성

new를 사용하는 것은 구상 클래스의 인스턴스를 만드는 것입니다. 구상 클래스를 바탕으로 코딩을 하면 나중에 코드를 수정해야할 가능성이 높아지고, 유연성이 떨어지게 됩니다.

그 예가 아래와 같습니다. code reference

구상 클래스를 사용하면 아래처럼 조건에 따라 만들려고 하는 구상 클래스를 명시해줘야 합니다. 이는 뭔가 변경하거나 확장해야할 때 코드를 또 확인하고 추가해야한다는 뜻이죠. 이걸 다른 메소드 또는 객체로 클래스 생성을 위임하여 역할을 분리하는 것이 바로 팩토리 패턴입니다.

Pizza orderPizza(String type) {
      Pizza pizza;

      if (type.equals("cheese")) {
          pizza = new CheesePizza();
      } else if (type.equals("greek")) {
          pizza = new GreekPizza();
      } else if (type.equals("pepperoni")) {
          pizza = new PepperoniPizza();
      }

      pizza.prepare();
      pizza.bake();
      pizza.cut();
      pizza.box();
      return pizza;
}

자, 이제 간단한 팩토리를 만들어 이를 클래스 생성을 위임해 봅시다.

이 작업 전에 바뀌는 부분과 안바뀌는 부분을 구분해야 합니다. 변하는 부분은 pizza 인스턴스를 생성하는 부분, 변하지 않는 부분은 나머지 작업이네요. 하나의 factory class를 생성하여 변하는 부분을 다른 클래스로 분리합니다.

code reference link

public class SimplePizzaFactory {
    public Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("greek")) {
            pizza = new GreekPizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }

        return pizza;
    }
}

위처럼하면 SimplePizzaFactory가 클라이언트가 많을 때에 빛을 발휘합니다. 만약 SimplePizzaFactory가 없었다면 모든 클라이언트에서 수정 작업을 해줘야 합니다.

팩토리 메소드 패턴(Factory Method Pattern)

code reference

팩토리 메소드 패턴(Factory Method Pattern)은 추상 메소드를 이용하여 생성하는 객체를 서브 클래스에서 결정하게 구현하는 것입니다.

PizzaStore.java

public abstract class PizzaStore {

	Pizza orderPizza(String type) {
	        Pizza pizza;
	        pizza = createPizza(type);
	
					/* 피자를 준비하고 굽고 커팅하는 과정 */
	    }
			/* PizzaStore에서는 만들 피자를 결정하지 않고 서브 클래스에서 결정 */
	    public abstract Pizza createPizza(String type);
	}
}

ChicagoPizzaStore.java

public class ChicagoPizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String type) {
        if (type.equals("cheese")) {
            return new ChicacoStyleCheesePizza();
        } else if (type.equals("greek")) {
            return new ChicagoStyleGreekPizza();
        } else if (type.equals("pepperoni")) {
            return new ChicagoStylePepperoniPizza();
        } else {
            return null;
        }
    }
}

팩토리 메소드는 객체 생성을 처리하며, 팩토리 메소드를 이용하면 객체를 생성하는 작업을 서브클래스에 캡슐화시킬 수 있습니다. 이렇게 하면 수퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 분리시킬 수 있습니다.

팩토리 메소드 패턴의 정의는 아래와 같습니다.

팩토리 메소드 패턴 - 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것이죠.

간단한 팩토리 패턴은 일회성으로 사용가능한 반면, 팩토리 메소드 패턴을 이용하면 어떤 구현을 사용할지를 서브 클래스에서 결정하는 프레임워크를 만들 수 있습니다.

의존성 뒤집기 원칙(Dependency Inversion Principle)

Design Principle 6.
추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다.

고수준 구성요소가 저수준 구성요소에 의존하면 안됩니다.

예를 들어, 예시에서 Pizza 라는 상위 클래스가 없었다면 PizzaStore 클래스는 여러 피자 종류를 감당하고 여러 피자 종류의 클래스에 의존하고 있었을 것입니다. 하지만 Pizza 라는 상위 클래스를 생성함으로써 PizzaStorePizza 를 구성으로 가지고 있고 나머지 피자 종류 클래스는 Pizza 에 의존하게 됩니다.

원칙을 지키는 데 도움이 될만한 가이드라인

  • 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 맙시다.

  • 구상 클래스에서 유도된 클래스를 만들지 맙시다.

    → 구상 클래스에서 유도된 클래스를 만들면 특정 구상 클래스에 의존하게 됩니다.

  • 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드하지 맙시다.

    → 오버라이드가 필요한 경우는 추상화가 잘못된 경우라고 볼 수 있습니다.

추상 팩토리 패턴(Abstract Factory Pattern)

추상 팩토리 패턴 - 추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된 , 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.

추상 팩토리 패턴을 사용하면 클라이언트에서 추상 인터페이스를 통해 제품을 공급받을 수 있습니다. 이를 통해 클라이언트에서는 구성 클래스에 대한 의존성을 분리할 수 있습니다.

다이어그램을 확인해보면 Client에서는AbstractProductA, AbstractProductB 를 구성하고 있습니다. Client 는 실제 어떤 구성 클래스를 포함하고 있는지 알지 못합니다.

이 구성 변수는 Client 코드 상에는 인터페이스를 통해 추상화시키고, 실제 객체 구상 클래스는 AbstractFactory 를 구현한 하위 클래스(ConcreteFactor1 인지 ConcreteFactory2)에서 지정합니다.

profile
재밌는 것만 하고 싶어 ʕ•ﻌ•ʔ

0개의 댓글