JAVA/디자인 패턴

[Simple Factory] 간단한 팩토리

dev_rosieposie 2023. 7. 7. 15:03

Goal 

  1. 간단한 팩토리에 대해 알아본다
  2. 샘플 코드를 통한 객체의 캡슐화를 간단한 팩토리로 변경해본다
  3. 다양한 팩토리 만들어보기 
  4. new 연산자에 대한 고찰

 

간단한 팩토리 (Simple Factory) 

디자인 패턴이라기 보다는 프로그래밍에서 자주쓰이는 관용구에 가깝다. 

 

 

왜 or 언제 간단한 팩토리 사용하는가?

객체 생성을 처리하는 부분을 Factory라고 부르며, 해당 부분을 캡슐화하여 분리할 수 있기 때문에 코드 가독성이나 관리 측면에서 용이하다

 

다이어그램

간단한 팩토리

 

 최첨단 피자 코드 만들기 

 as - is  피자 주문 코드 

public Pizza orderPizza(String type) {
    Pizza pizza = new Pizza();

// 바뀌는 코드
if(type.equals("cheese")){
    	pizza = new CheesePizza();
    }else if(type.equals("greek")){
    	pizza = new GreekPizza();
    }else if(type.equals("peperoni")){
    	pizza = new PeperoniPizza();
    }

// 바뀌지 않는 코드
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();

    return pizza;
}

 => 피자 종류를 바탕으로 type을 받아 구상 클래스의 인스턴스를 만들고 pizza 인스턴스 변수에 그 인스턴스를 대입한다.

문제점 :  피자 종류가 추가 또는 변경될 때마다 바뀌는 코드를 계속 수정해야함. 

 

 to-be  피자 주문 코드 

객체 생성 부분 캡슐화

public Pizza orderPizza(String type) {
    Pizza pizza;

    pizza = factory.createPizza(type);

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

    return pizza;
}

pizza = factory.createPizza(type);

 

orderPizza()에서 가장 문제가 되는 부분인 인스턴스를 만드는 구상 클래스를 선택하는 부분으로, 바뀌는 코드를 캡슐화한다.

객체 생성을 처리하는 클래스 Factory - SimplePizzaFactory

public class SimplePizzaFactory {
    public Pizza createPizza(String type){

        Pizza pizza = null;

	// orderPizza()에서 뽑아온 코드
        // 전달 받은 매개변수로 피자 종류를 결정 
        
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }else if(type.equals("peperoni")){
            pizza = new PeperoniPizza();
        }else if(type.equals("clam")){
            pizza = new ClamPizza();
        }else if(type.equals("veggie")){
            pizza = new VeggiePizza();
        }else {
            pizza = null;
        }
        return pizza;
    }
}

 

  • 이 클래스에서 하는 일은 클라이언트가 받을 피자만 만드는 일만한다.
  • SimplePizzaFactory를 만들고 나면 orderPizza() 메소드는 새로 만든 객체의 클라이언트가 된다.
  • 즉, 새로운 만든 객체를 호출하게 되는것 => 피자가 필요할 때마다 피자 공장에 피자 만들어 달라고 부탁하면 된다.
  • orderPizza() 메소드에서 어떤 피자를 만들지 고민하지 않아도 됨

클라이언트 코드

public class PizzaStore {

    // PizzaStore에 SimplePizzaFactory의 레퍼런스를 저장
    SimplePizzaFactory factory;

    // PizzaStore의 생성자에 팩토리 객체가 저장됨
    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    // orderPizza() 메소드는 팩토리로 피자 객체를 만들고, 주문받은 형식을 이 쪽으로 전달하면 됨
    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = factory.createPizza(type);

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

        return pizza;
    }
}
  • new 연산자 대신 팩토리 객체에 있는 create 메소드를 사용하여, 더 이상 구상 클래스의 인스턴스를 만들 필요가 없다

 

다양한 팩토리 만들기 

상황 

위의 피자가게가 대박이나서 뉴욕, 시카고, 캘리포니에서도 지점을 오픈하고자한다.

요구 사항

각 지점마다 그 지역의 특성과 입맛(뉴욕, 시카고, 캘리포니아 스타일) 을 반영한 다양한 스타일의 피자를 만들어야 한다.

해결 방안

SimpleFactory를 삭제하고 3가지의 서로 다른 다양한 팩토리(뉴욕 피자 팩토리, 시카고 피자 팩토리, 캘리포니아피자 팩토리)를 만들어 PizzaStore에서 적당한 팩토리를 사용한다.

SimplePizzaFactory 대신, 지역 별 PizzaFactory만들기

PizzaStore nyStore = new NYPizzaStore();			// 뉴욕 지점
PizzaStore chicagoStore = new ChicagoPizzaStore();		// 시카고 지점

 

 

하지만 지점들을 제대로 관리하기 위해, PizzaStore와 피자 제작 코드 전체를 하나로 묶어주는 프레임을 만들어야 한다. 

  • 굽는 방식이 달라진다거나, 이상하게 생긴 피자 상자를 쓴다거나 컷팅을 까먹는 일 발생
  • SimplePizzaFactory를 만들기 전에 썼던 코드에는 피자를 만드는 코드가 PizzaStore와 직접 연결되었지만, 유연성 x

=> 팩토리 메소드 패턴을 사용하여 극복한다.

 

new 연산자 

new를 사용하면 구상 클래스의 인스턴스가 만들어진다. 인터페이스가 아닌, 특정 구현을 사용하는 것이다.

new  권장 코드

구상 클래스를 바탕으로 코딩하면 나중에 코드를 수정해야 할 가능 성이 커지고, 유연성이 떨어지므로 인터페이스를 사용한다.

Duck duck = new MallardDuck();                    //  권장 o
MallardDuck duck = new MallardDuck();       //  권장 x

new 의  문제

new의 문제가 아닌 '변화'의 문제로, 변화하는 무언가 때문에 new를 조심해서 사용해야한다.

  • 인터페이스에 맞춰 코딩하면 시스템의 여러 변화에 대응할 수 있다
    • 인터페이스 바탕 코드는 어떤 클래스든 특정 인터페이스만 구현하면 사용할 수 있기 때문 => 다형성
  • 구상 클래스를 많이 사용하면 새로운 구상 클래스가 추가될 때마다 코드를 고쳐야함 
    • 변경에 닫혀있는 코드가 됨. 새로운 구상 형식을 써서 확장 해야할 때는 다시 열 수 있게 해야함 => OCP 개방폐쇄의 원칙

 

참고

new로 객체를 사용하면 RDMS에서 not null인 필드의 경우도 null로 생성이 되는 문제가 발생한다.

그래서 이 객체를 생성하는 대부분의 경우에는 빌더패턴을 사용하는 것을 권장한다.

 

 

 

 

참고

헤드퍼스트 디자인 패턴