객체 생성을 처리하는 클래스를 팩토리라고 부릅니다.

Simple Factory
객체를 생성하는 일을 전담하는 클래스

public class PizzaStore {
    Pizza orderPizza(String type) {
        Pizza pizza;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }
        pizza.prepare(); 
        pizza.bake(); 
        piaaz.cut();
        pizza.box();
        reutnr pizza;
    }
}

상황에 따로 코드가 변경되기 때문에 바뀌는 부분과 바뀌지 않는 부분을 분리 한 후 캡슐화를 한다.
Simple Factory 적용해서 위와 같은 문제점을 수정한다.
public class SimplePizzaFactory {
    public Pizza create Pizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}

분점에서 PizzaStore 를 활용하여 NYPizzaFactory라는 factory 를 만들어서 뉴욕식 피자를 만드는 팩토리를 사용한다.
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.order("cheese");


문제점
분점에서 독자적인 방법들을 사용하기 시작했습니다.
굽는 방식이 달라진다거나 피자를 자르는 것을 까먹어 버리는일, 이상한 피자 상자를 사용 등
그래서 피자 가게와 피자 제작 과정 전체를 하나로 묶어주는 프레임워크를 만들어야 겠다는 결론에 도달했습니다.

피자를 만드는 활동 자체는 전부 PizzaStore 클래스에 국한시키면서도 분점마다 고유의 스타일을 살릴 수 있도록 하는 방법이 있습니다.

createPizza() 를 PizzaStore에 다시 넣고 그 메소드를 추상 메소드로 선언하고, 각 분점마다 고유의 스타일에 맞게 PizzaStore의 서브클래스를 만들도록 할 것입니다.

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    abstract Pizza createPizza(String type);
}

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

정의

객체를 생성하기위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것이죠.
-> 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야 할 때, 어떤 구상 클래스를 필요로 하게 될지 미리 알 수 없는 경우에도 매우 유용합니다.

"메서드 팩토리 패턴에서는 어떤 클래스의 인스턴스를 만들지를 서브클래스에서 결정한다?
서브클래스에서 실제로 뭔가를 "결정"하는 것이 아니라, 우리가 선택하는 PizzaStore의 서브클래스 종류에 따라 결정되는 것이지만, 만들어지는 피자의 종류를 해당 서브클래스에서 결정한다고 할 수도 있겠죠
수퍼클래스에 있는 orderPizza()에서는 어떤 피자가 만들어 지는지 전혀 알 수 없다는 점을 잘 이해해야 합니다.
Pizza 인스턴스를 만드는 일을 이제 팩토리 역할을 하는 메소드에서 맡아서 처리 합니다.
팩토리 메소드는 객체 생성을 처리하며, 팩토리 메소드를 이용하면 객체를 생성하는 작업을 서브클래스에 캡슐화시킬 수 있습니다. 수퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 분리시킬 수 있습니다.

모든 팩토리 패턴에서는 객체 생성을 캡슐화합니다. 팩토리 메소드 패턴에서는 서브클래스에서 어떤 클래스를 만들지를 결정하게 함으로써 객체 생성을 캡슐화 합니다.

객체 생성 코드를 전부 한 객체 또는 메소드에 집어넣으면 코드에서 중복되는 내용을 제거할 수 있고, 나중에 관리할 때도 한군데에만 신경을 쓰면 됩니다. 구현이 아닌 인터페이스를 바탕으로 프로그래밍을 할 수 있게 되고, 그 결과 유연성과 확장성이 뛰어난 코드를 만들 수 있게 됩니다.

Simple Factory 와 Factory Method 차이점
간단한 팩토리를 사용할 때는 팩토리가 PizzaStore 안에 포함되는 별개의 객체이다.
팩토리 메서드 패턴을 이용하면 어떤 구현을 사용할지를 서브클래스에서 결정하는 프레임워크를 만들 수 있다는 결정적인 차이점이 있다.
간단한 팩토리에서는 객체생성을 캡슐화하는 방법을 사용하긴 하지만 팩토리 메소드 패턴처럼 강력한 유연성을 제공하지는 못한다. 생성하는 제품을 마음대로 변경할 수 없기 때문이다.

객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)

3번과 6번이 비슷하지만 의존성 뒤집기 원칙에서는 추상화를 더 많이 강조 합니다. 이 원칙에는 고수준 구성요소가 저수준 구성요소에 의존하면 안 된다는 것이 내포되어 있습니다. 항상 추상화에 의존하도록 만들어야 합니다.
고수준 구성요소는 다른 저수준 구성요소에 의해 정의되는 행동이 들어있는 구성요소를 뜻합니다.
예를 들어, PizzaStore의 행동은 Pizza에 의해 정의되기 때문에 PizzaStore는 고수준 구성요소라고 할 수 있고, Pizza 는 저수준 구성요소라고 할 수 있습니다.

의존성 뒤집기 원칙을 지키는데 도움이 될만한 가이드 라인
- 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 맙시다.
- 구상 클래스에서 유도된 클래스를 만들지 맙시다.
- 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 맙시다.


추상 팩토리 패턴

정의
인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.
구상 클래스는 서브 클래스에 의해 만들어 집니다.
-> 클라이언트에서 서로 연관된 일련의 제품들을 만들어야 할 때, 즉 제품군을 만들어야 할 때

팩토리 메소드 패턴과 추상 팩토리 패턴
두 패턴 모두 객체 생성을 캡슐화해서 애플리케이션의 결합을 느슨하게 만들고, 특정 구현에 덜 의존하도록 만들 수 있다.
팩토리 메소드 패턴
- 상속을 통해서 객체를 생성
- 클라이언트와 구상 형식을 분리시켜주는 역할을 한다.
- 서브클래스에서 만드는 구상 형식을 활용하는 추상 생산자에서 코드를 구현한다.
- 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야 할 때, 어떤 구상 클래스를 필요로 하게 될지 미리 알 수 없는 경우에도 유용하다.

추상 팩토리 패턴
- 객체 구성을 통해서 생성
- 일련의 연관된 제품을 하나로 묶을 수 있다.
- 제품을 추가하려면 인터페이스를 바꾸어야 한다.
- 팩토리 메소드 패턴을 사용하여 제품을 생성하는 경우가 있다.
- 클라이언트에서 서로 연관된 일련의 제품들을 만들어야 할 때, 즉 제품군을 만들어야 할 때 사용

핵심 정리
- 팩토리를 쓰면 객체 생성을 캡슐화할 수 있습니다.
- 간단한 팩토리는 엄밀하게 말해서 디자인 패턴은 아니지만, 클라이언트와 구상 클래스를 분리시키기 위한 간단한 기법으로 활용할 수 있습니다.
- 팩토리 메소드 패턴에서는 상속을 활용합니다. 객체 생성이 서브클래스에게 위임되죠. 서브클래스에서는 팩토리 메소드를 구현하여 객체를 생산합니다.
- 추상 팩토리 패턴에서는 객체 구성을 활용합니다. 객체 생성이 팩토리 인터페이스에서 선언한 메소들에서 구현되죠.
- 모든 팩토리 패턴에서는 애플리케이션의 구상 클래스에 대한 의존성을 줄여줌으로써 느슨한 결합을 도와줍니다.
- 팩토리 메소드 패턴에서는 어떤 클래스에서 인스턴스를 만드는 일을 서브클래스한테 넘깁니다.
- 추상 팩토리 패턴은 구상 클래스에 직접 의존하지 않고도 서로 관련된 객체들로 이루어진 제품군을 만들기 위한 용도로 쓰입니다.
- 의존성 뒤집기 원칙을 따르면 구상 형식에 대한 의존을 피하고 추상화를 지향할 수 있습니다.
- 팩토리는 구상 클래스가 아닌 추상 클래스/인터페이스에 맞춰서 코딩할 수 있게 해주는 강력한 기법입니다.
Posted by outliers
,