팩토리 패턴

강한친구·2022년 4월 5일
0

OOP Desing Pattern

목록 보기
6/15


출처

구상객체

Member member = new memberRepoistory()

우리는 객체를 새로 member에 선언할 때, new를 사용한다. new라는것은 구성객체를 의미한다.
앞에서 말한것처럼 구성객체에 의존한느 코딩은 좋은 코딩이 아니다.

이렇게 작성을 하면 작동은 하겠지만, OCP원칙을 지키지 못하게 되고, 새로운 구현 클래스가 추가 될 때 마다 (변화) 코드를 고쳐야하는 변화에 닫혀있는 코드가 되어버린다. 따라서 일단 변화를 하는 부분만 때서 캡슐화하여야 한다.
이러한 과정을 심플 팩토리 라고 한다.

Simple Factory

심플 팩토리 패턴이란, 객체를 생성하는 공장을 따로 만드는것이다.

Pizza객체는 밑에 나온다.

public class PizzaStore {
    SimplePizzaFactory factory;
    public PizzaStore (SimplePizzaFactory factory) {
        this.factory = factory;
    }
    public Pizza orderPizza (String type) {
        Pizza pizza; pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
public class PizzaFactory {
    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if( type.equals("cheese") )
            pizza = new CheesePizza();
        else if( type.equals("pepperoni") )
            pizza = new PepperoniPizza();
        else if( type.equals("calm") )
            pizza = new ClamPizza(); return pizza;
    }
}

이렇게 피자주문은 받는 pizzaStore객체와, 이를 생성하는 pizzaFactory를 만들어서 구분하는것이 바로 심플팩토리 패턴이다.

이는 객체와 객체생성을 구분하여 객체생성부분에 변화가 생기더라도 쉽게 변경할 수 있도록 하는 장점이 있다.

Factory Method Pattern

이번에 만들 코드는 피자만드는 코드이다.

package pizzaria;

import java.util.ArrayList;

public abstract class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList toppings = new ArrayList();

    void prepare() {
        System.out.println("Preparing " + name);
        System.out.println("Tossing Dough...");
        System.out.println("Adding sauce...");
        System.out.println("Adding topppings: ");
        for (Object topping : toppings) {
            System.out.println(" " + topping);
        }
    }
    void bake() {
        System.out.println("Bake for 25 mins at 350");
    }
    void cut() {
        System.out.println("cutting the pizza into diagonal slices");
    }
    void box() {
        System.out.println("Place pizza in official Pizzastore Box");
    }
    public String getName() {
        return name;
    }
}

피자의 추상 클래스이다.

package pizzaria;

public abstract class PizzaStore {
    public Pizza orderPizza(String type) {
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    protected abstract Pizza createPizza(String type);
}

이는 피자가게별로 차별화를 두기 위한 추상클래스이다.
이 추상클래스에서 우리는 createPizza를 팩토리 처리한다.

팩토리화를 하기 위해 protected로 보호하고, 따로 구현클래스를 만든다.

createPizza를 담당하는 객체를 만들어서 해당 객체에서 모든걸 처리하게 만드는것이다. 이런 디자인을 통해 우리는 createPizza를 다른 pizzaStore에서도 쓸 수 있고, 만약 변동이 생기더라도 보호할 수 있다.

package pizzaria;

public class NYPizza extends PizzaStore{

    public Pizza createPizza(String item) {
        if (item.equals("cheese")) return new NYstyleCheesePizza();
        else return null;
    }
}

이 코드는 createPizza를 구체적으로 구현하는 코드이다.

package pizzaria;

public class NYstyleCheesePizza extends Pizza {
    public NYstyleCheesePizza() {
        name = "NY Style Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";

        toppings.add("Grated Reggiano Cheese");
    }

    @Override
    void cut() {
        System.out.println("Cut in Triangle");
    }
}

이 코드는 선택된 피자에 대한 또 하나의 구체적인 구현 코드이다.

package pizzaria;

public class PizzaTestDriver {
    public static void main(String[] args) {
        PizzaStore ps = new NYPizza();
        Pizza pizza = ps.orderPizza("cheese");
        System.out.println(pizza.getName() + "\n");
    }
}

이를 전체적으로 실행하면 다음과 같다.

  1. 피자스토어를 결정함 (NYPizza)
  2. 해당 스토어에서 pizza객체를 통해 cheese피자를 주문함
  3. orderPizza 객체에서 createPizza를 호출, NYPizza에 있는 createPizza가 작동함
  4. if문 조건에 맞춰서 알맞은 클래스를 반환함 (NYStyleCheesePizza)

유의할 점
1. 피자객체를 반환하기때문에 함수들이 Pizza createPizza의 형태이다.
2. 완벽한 OCP, DIP은 아니다.

설명

팩토리 메소드 패턴에서는, 객체생성을 담당하는 factory 부분을 인스턴스화하고, 객체생성은 서브클래스에게 위탁하는 방식이다.

우리는 생성메소드인 createPizza()를 추상메소드로 선언하였고, 각 피자스토어에서 이를 관리하도록 설정하였다. 이러한 구조가 팩토리 메소드 패턴이다.

이러한 패턴을 사용하는것으로, pizzaStore의 전체적인 틀 (prepare, box, cut, dough)은 지키면서 동시에 각 객체별로 특성을 가질 수 있게 되었다.

모든 팩토리 패턴에서는 객체 생성을 캡슐화한다.

Creator

PizzaStore와 이를 상속하는 각 피자가게들은 생산자 클래스이다.

Product

Pizza의 틀을 잡는 클래스와 그 하위 피자들 종류는 전부 제품 클래스이다.

Creator가 공장이고, Product가 공장에서 생산되는 상품으로 이해하면 쉽다

이 둘은 orderPizza라는 메소드로 서로 연결되는데 이를 병렬적으로도 표현할 수 있다.

객체 의존성과 객체 뒤집기

의존적인 객체들은 OCP원칙을 위배한다.
한쪽이 변하면 다른 한쪽도 변해야하고 이는 코드변경 수요가 너무 많아지기 때문이다.

따라서 수정이 일어나기 쉬운 부분과 아닌 부분을 분리해서 작성하는것이 중요하다. 그래서 이전글에서 정리한것처럼 변경이 자주 일어날 수 있는 부분은 추상화하여 각 매장클래스에서 구현하게 하였고 나머지는 추상클래스에서 처리하였다.

DIP

의존성 뒤집기 원칙이다. 추상하된 것에 의존해야지, 구체화된 클래스에 의존해서는 안된다는 뜻이다.
이전글에서 작성한 코드는 이러한 의존관계를 가지고 있다.

이를 팩토리 메소드를 써서 바꾸면 Pizza 추상클래스가 존재하고 , 나머지 모든 클래스들은 pizza에 의존하도록 변경되어야한다.

물론 이 모든것을 100% 지키기는 어렵고 사실 그럴 필요도 없다고한다. 가능하면 지키는것이 좋고 최대한 변경이 있을 부분들은 캡슐화하자는 의미이다.

0개의 댓글