2015년 8월 27일 목요일

클래스 디자인 패턴 4. Factory Pattern_

1. 개념
  1) 발생 원인 :
     사용클래스 에서 뭔가 객체를 만들어 사용하려는데 기본적인 방법은
       new로 인스턴트를 만들어 사용하는 방법이다.
     근데 원본 클래스(기능구현 클래스)가 늘어나고 때에 따라 구현체
       선택이 자주 바뀐다면 음 고민되는 상태임
     물론 인터페이스 생성을 통해서 생성하고 있지만 조건에 따라
       수정이 필요한 부분도 있고, 어떤 구현체를 쓸지 분기를 따야
       될 수도 있음.
     수정이 커지면 해당 인스턴스 구현부에 관계된 부분에 소스가
       추가되고, 복잡해지고, 관계 없는것도 섞이고 실제 구현체도
       수정하는데 갖다 쓰는데도 수정해야되? 그리고 갖다 쓰는데가
       한 두군데로 끝나지 않을 수도 있지 않아?
      줄임 : 어떻게 해야 애플리케이션에서 구상 클래스의 인스턴스를
        만드는 부분만을 떼어내고, 캡슐화시킬 수 있을까?
  
   
  2) 정리
      그러면 구현시 사용 클래스를 상속하는 하단 클래스에서
        어떤 클래스인지 해당 클래스에서 작업해야 되는걸 분리
        할 수 있다면 수정이 이루어져도 사용하는 여러 작업이
        포함된 클래스를 건들지 않고 클래스는 좀더 늘었지만
      해당 작업에 좀더 관계된 클래스만 수정하여 원하는 효과를 
        얻을 수 있지 않을까? 라고 생각했다.

2. 소스
1> 간단한 형태의 구현
  1) 우선 Pizza랑 그 구현체 class에 대해서는 나중에 구현하고
       (Pizza는 interface일꺼고, 메서드4개는 최소한 정의되어 있을거고,
       구현체 피자들은 Pizza를 implements 했겠지)

      main 만 보자 여기에서는 orderPizza를 호출하는데 
        이때 타입 변수를 할당한다. 근데 이게 그때 그때
        뭘 호출할지 다른데

      막상 orderPizza 에 가보니 if 구문으로 문자열에
        따라 할당되어 있고, 그 밑에 공통함수도 같이 존재
        하고 있는 상태임.
 
public class PizzaStore {

  public static void main(String[] args) {
  orderPizza("cheese");
 }
 
 static Pizza orderPizza (String type){
  Pizza pizza=null;
  
  if(type.equals("cheese")){
   pizza = new CheesePizza();
  }else if(type.equals("pepperoni")){
   pizza = new PepperoniPizza(); 
  }else if(type.equals("clam")){
   pizza = new ClamPizza(); 
  }else if(type.equals("veggie")){
   pizza = new VeggiePizza(); 
  }
  
  pizza.prepare();
  pizza.bake();
  pizza.cut();
  pizza.box();
  
  return pizza;
 }
}


  2) orderPizza() 에서 if 구문만 빼서 따로 클래스를 생성한다.
        클래스 이름은 SimplePizzaFactory 라고 하고 
        안에 createPizza(type) 함수를 명시하자.
       if구문 하나있는거 떼어놓았는데 뭐가 틀리냐고?
        실 구동 클래스 main에 if가 하나만 있을리가 없다가 아닐까?
       각각의 결정이 필요한 분기문들이 하나의 클래스로 생성되고
        작성되면 당연하게 복잡도가 감소한다.
 
public class SimplePizzaFactory {
 static 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("clam")){
   pizza = new ClamPizza(); 
  }else if(type.equals("veggie")){
   pizza = new VeggiePizza(); 
  }
  
  return pizza;
 }
}


  3) 그럼 원본 클래스도 바뀐다. 이렇게 멤버변수로 
       생성자에 포함시켜 쓸수 있지만 상황에 따라 작성하면 될거고
       이제는 피자 종류가 늘어나도 PizzaStore를 고칠 필요없이
       SimplePizzaFactory 만 수정하면 원하는 목적을 이룰 수 있게 됨.
     다만 밑에 실행되는 함수는 해당 피자가 가지고 있는 함수랑
       동일하기 때문에(여기서는) 함수가 달라지면 역시 수정은
       필요하게 된다.
 
public class PizzaStore {
 SimplePizzaFactory factory;
 
// public PizzaStore(SimplePizzaFactory factory){
//  this.factory=factory;
// }
 
 public static void main(String[] args) {
  orderPizza("cheese");
 }
 
 static Pizza orderPizza (String type){
  Pizza pizza= SimplePizzaFactory.createPizza("cheese");
  
  pizza.prepare();
  pizza.bake();
  pizza.cut();
  pizza.box();
  
  return pizza;
 }
}


2> java Design 예제
  1) interface 를 각각 작성
 
public abstract class Product {
 public abstract void use();
}

 
public abstract class Factory {
 
 public final Product create(String owner){
   Product p = createProduct(owner);
   registerProduct(p);
   return p;
 }
 
 protected abstract Product createProduct(String owner);
 protected abstract void registerProduct(Product product);
}


  2) Product 를 상속받은 IDCard 클래스를 작성
 
public class IDCard extends Product {
 private String owner;
 
 IDCard(String owner){
  System.out.println(owner+"'s making card");
  this.owner = owner;
 }

 @Override
 public void use() {
  System.out.println(owner +"'s use card");
 }
 
 public String getOwner(){
  return owner;
 }
}


  3) Factory 를 상속하고 IDCard를 사용하는 IDCardFactory
      클래스를 작성
 
import java.util.Vector;

public class IDCardFactory extends Factory {
 private Vector owners = new Vector();
 
 @Override
 protected Product createProduct(String owner) {
  return new IDCard(owner);
 }

 @Override
 protected void registerProduct(Product product) {
  owners.add(((IDCard)product).getOwner());
 }

 public Vector getWoners(){
  return owners;
 }
}


  4) 테스트 코드를 작성
 
public class MainTest {

 public static void main(String[] args) {
  Factory factory = new IDCardFactory();
  Product card1 = factory.create("hong");
  Product card2 = factory.create("kim");
  Product card3 = factory.create("kang");
  
  card1.use();
  card2.use();
  card3.use();
 }
}


실행 : 
hong's making card
kim's making card
kang's making card
hong's use card
kim's use card
kang's use card


3. 다이어그램
    이번엔 소스먼저 확인했는데 이제 PizzaStore 에서는
      new는 SimplePizzaFactory 만 쓰고, 구현체 원본인 Pizza와
      그 밑의 잡다한 상세피자는 간접적으로만 관계를 맺게 되었다.

4. 관련있는 패턴
  1) Template Method 패턴
     Factory Pattern은 해당 패턴을 응용한 형태입니다.

  2) Singleton 패턴
     Create 를 이용하는 방식이 같습니다. 여기서는 Singleton이
      아니지만 쉽게 Sigleton으로 변경이 가능합니다.

  3) Composite 패턴
     Product 역할에 변화를 줘서 Composite 패턴을 적용 가능합니다.

  4) Iterator 패턴
     Iterator 패턴에서 iterator 메서드가 Iterator 인스턴스 작성시
      Factory Method패턴이 사용되는 경우가 있습니다.

댓글 없음:

댓글 쓰기