본문 바로가기

디자인패턴

[디자인 패턴] 심플 팩토리, 팩토리 메소드, 추상 팩토리

정의

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

결론

객체 생성 하는 코드를 분리하여 클라이언트 코드와 결합도(의존성)를 낮추어 코드를 건드리는 횟수를 최소화 하기 위한 패턴이다.

결합도는 왜 낮아지는가?

그것은 객체지향 성질중에 하나인 다형성을 이용 하였기 때문이다. 인터페이스를 구현한 객체들은 같은 인터페이스를 바라 보기 때문에 코드에 유연함이 있기 때문이다.

왜 코드를 건드리는 횟수가 최소화 될까?

객체를 생성하는 코드 부분을 분리 시켰기 때문에 객체를 추가/수정이 일어 나더라도 객체를 생성하는 코드만 건들면 되기 때문이다.

일단, 객체를 생성하는 일은 구상클래스를 만드는 일인데 새로운 객체가 추가 될 수도 있고 삭제 될 수 있는 가능성이 상대적으로 많다.
또한 객체를 생성하는 코드가 여러 클래스내 에서도 사용 되면 예를 들어 10개의 클래스 내에서 객체를 생성하는 if ~ else if 구문이 있다고 했을때 객체 추가/수정이 발생하면 10개의 클래스에 코드를 변경해 줘야 하는 일이 발생한다.

 

바로 이러한 문제점을 해결하기 위해 팩토리 패턴을 사용하는 것이다.

 

예시

public abstract class Pizza {

    public abstract void prepare();

    public void bake() {
        System.out.println("baking pizza for 12min in 400 degrees..");
    }
    public void cut() {
        System.out.println("cutting pizza in pieces");
    }
    public void box() {
        System.out.println("putting pizza in box");
    }

    public Pizza orderPizza(String pizzaType) {
        Pizza pizza = null;

        /**
         * 자주 변경 되는 부분..
         */
        if("Cheese".equals(pizzaType)) {
            pizza = new CheesePizza();
        } else if ("Greek".equals(pizzaType)) {
            pizza = new GreekPizza();
        }

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

        return pizza;
    }
}

public class GreekPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("preparing a greek pizza..");
    }
}

public class CheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("preparing a cheese pizza..");
    }
}

 

음식 트렌드가 바뀌면 조건문을 자주 변경해야 한다. 가까운 장래에 피자 유형을 더 추가하고 인기가 없는 피자를 몇 개 제거될 수 있다. 현재 디자인은 수정을 위해 열려 있기 때문에 단일 책임 원칙을 위반

Simple Factory (심플 팩토리)

피자

 

public abstract class Pizza {

    public abstract void prepare();

    public void bake() {
        System.out.println("baking pizza for 12min in 400 degrees..");
    }
    public void cut() {
        System.out.println("cutting pizza in pieces");
    }
    public void box() {
        System.out.println("putting pizza in box");
    }
}

public class GreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a greek pizza..");
    }
}

public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a cheese pizza..");
    }
}

 

피자공장

 

public class SimplePizzaFactory {

    public Pizza createPizza(String pizzaType) {
        Pizza pizza = null;

        if("Cheese".equals(pizzaType)) {
            pizza = new GreekPizza();
        } else if ("Greek".equals(pizzaType)) {
            pizza = new CheesePizza();
        }

        return pizza;
    }
}

 

피자 가게

 

public class PizzaStore {
    private SimplePizzaFactory simplePizzaFactory;

    public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
        this.simplePizzaFactory = simplePizzaFactory;
    }

    public Pizza orderPizza(String pizzaType) {
        Pizza pizza = simplePizzaFactory.createPizza(pizzaType);

        pizza.prepare();;
        pizza.bake();
        pizza.cut();

        return pizza;
    }
}

 

피자 생성을 새 객체로 분리하였다. 우리는 그 객체를 팩토리라고 부른다. 이 새 개체는 변경되는 모든 것을 포함한다.(이 경우 피자 유형에 따라 피자 생성 프로세스). 아래는 현재 개선된 디자인의 클래스 다이어그램이다.

 

Simple Factory

 

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

우리의 현재 디자인에는 여전히 몇 가지 문제가 있다. 예를 들어 New York/Chicago/California 스타일의 피자를 만들고자 한다면 어떻게 될까? 이를 해결하는 한 가지 방법은 다음과 같다.

 

피자 - simple factory와 동일 종류가 많아짐..

 

public abstract class Pizza {

    public abstract void prepare();

    public void bake() {
        System.out.println("baking pizza for 12min in 400 degrees..");
    }
    public void cut() {
        System.out.println("cutting pizza in pieces");
    }
    public void box() {
        System.out.println("putting pizza in box");
    }
}

public class NYStyleGreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a New York style greek pizza..");
    }
}

public class NYStyleCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a New York style cheese pizza..");
    }
}

public class ChicagoStyleGreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a Chicago style greek pizza..");
    }
}

public class ChicagoStyleCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("preparing a Chicago style cheese pizza..");
    }
}

 

피자가게 - 피자가게가 추상 클래스로 바뀌고, 피자가게를 상속하는 가게가 여러개 생김

 

public abstract class PizzaStore {

    public abstract Pizza createPizza(String pizzaType);

    public Pizza orderPizza(String pizzaType) {
        Pizza pizza = createPizza(pizzaType);

        pizza.prepare();;
        pizza.bake();
        pizza.cut();

        return pizza;
    }
}

public class NYPizzaStore extends PizzaStore {
    @Override
    public Pizza createPizza(String pizzaType) {

        Pizza pizza = null;

        if ("Greek".equals(pizzaType)) {
            pizza = new NYStyleGreekPizza();
        } else if ("Cheese".equals(pizzaType)) {
            pizza = new NYStyleCheesePizza();
        }

        return pizza;
    }
}


public class ChicagoPizzaStore extends PizzaStore {

    @Override
    public Pizza createPizza(String pizzaType) {

        Pizza pizza = null;

        if ("Greek".equals(pizzaType)) {
            pizza = new ChicagoStyleGreekPizza();
        } else if ("Cheese".equals(pizzaType)) {
            pizza = new ChicagoStyleCheesePizza();
        }

        return pizza;
    }
}

 

 

Factory Method Pattern

Simple Factory 와 다른 점

  • PizzaStore에서 createPizza() 를 추상 메소드로 정의하여 서브 타입 NYPizzaStore, ChicagoPizzaStore에서 구현하도록 하였다. 

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

피자에 들어가는 재료들을 생산하는 팩토리 생성.

코드가 너무 길어서 간단하게 뉴욕피자, 뉴욕 원재료, 치즈 피마까지만 구현함..

 

피자

 

public abstract class Pizza {

    protected Dough dough;
    protected Sauce sauce;
    protected Cheese cheese;
    protected Pepperoni pepperoni;

    public abstract void prepare();
    public void bake() {
        System.out.println("baking pizza for 12min in 400 degrees..");
    }
    public void cut() {
        System.out.println("cutting pizza in pieces");
    }
    public void box() {
        System.out.println("putting pizza in box");
    }
}

public class CheesePizza extends Pizza {
    private PizzIngredientFactory pizzIngredientFactory;

    public CheesePizza(PizzIngredientFactory pizzIngredientFactory) {
        this.pizzIngredientFactory = pizzIngredientFactory;
    }

    @Override
    public void prepare() {
        this.dough = pizzIngredientFactory.createDough();
        this.sauce = pizzIngredientFactory.createSauce();
        this.cheese = pizzIngredientFactory.createCheese();
        this.pepperoni = pizzIngredientFactory.createPepperoni();
    }
}

 

피자가게 

 

public abstract class PizzaStore {
    public abstract Pizza createPizza(String pizzaType);

    public Pizza orderPizza(String pizzaType) {
        Pizza pizza = null;
        pizza = createPizza(pizzaType);

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

        return pizza;
    }
}

public class NYPizzaStore extends PizzaStore {
    @Override
    public Pizza createPizza(String pizzaType) {

        Pizza pizza = null;
        PizzIngredientFactory pizzIngredientFactory = new NYPizzaIngredientFactory();

        if ("Greek".equals(pizzaType)) {
            //  Prepare the greek pizza here after creating
            //  the proper ingredients and then the GreekPizza
        } else if ("Cheese".equals(pizzaType)) {
            pizza = new CheesePizza(pizzIngredientFactory);
        }

        return pizza;
    }
}

 

원재료와 원재료 공장

 

public abstract class PizzIngredientFactory {
    public abstract Dough createDough();
    public abstract Sauce createSauce();
    public abstract Cheese createCheese();
    public abstract Pepperoni createPepperoni();
}

public class Dough {
}

public class Cheese {
}

public class Sauce {
}

public class Pepperoni {
}

 

미국 원재료

 

public class NYPizzaIngredientFactory extends PizzIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }

    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }
}

public class ThinCrustDough extends Dough {
}

public class MarinaraSauce extends Sauce {
}

public class ReggianoCheese extends Cheese {
}

public class SlicedPepperoni extends Pepperoni {
}