디자인패턴_AbstractFactory Pattern of Creational Pattern

2021. 1. 6. 15:362021/JOB DA STUDY

구체적인 클래스에 의존하지 않고, 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴

- 관련성 있는 여러 종류의 객체들을 일관된 방식으로 생성하는 경우에 유용

- Singleton pattern, FactoryMethod pattern을 사용

 

when

  • 객체가 생성, 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 하는 경우
  • 여러 제품군 중에서 하나를 선택해 시스템을 설정하고, 한번 구성한 제품을 다른것으로 대체할 수 있을때
  • 관련 제품 객체들이 함께 사용되도록 설계되어 있고, 이 부분에 대한 제약이 외부에도 지켜지도록 하고싶은 경우
  • 제품에 대한 class 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스 노출을 원할 때

 

bstract

AbstractFactory: 개념적 제품에 대한 객체를 생성하는 연산 (interface 제공)

ConcreteFactory: 구체적인 제품에 대한 객체를 생성하는 연산 (AbstractFatory의 추상 메서드를 오버라이드)

AbstractProduct: 개념적 제품 객체에 대한 인터페이스를 정의

ConcreteProduct: 구체적으로 팩토리가 생성할 객체를 정의하고, AbstractPrduct가 정의하는 인터페이스를 구현

 

엘리베이터 부품 업체 변경

여러 제조 업체의 부품을 사용하더라도 같은 동작을 지원하는 것이 바람직 -> 엘리베이터 프로그램의 변경을 최소화 

추상클래스 Motor 와 여러 제조업체의 Motor 클래스 <-연관관계-> 추상클래스 Door 와 여러 제조업체의 Door 클래스

 

Motor Class의 핵심 기능 : Move()

public void move(Direction direction){
 //1) 이미 이동 중인 경우, 무시

 //2) 문이 Open 상태라면, Close
 //3) 모터를 구동해서 이동 ----> 제조 업체에 따라 달라지는 부분
 //4) 모터의 상태를 이동중으로 설정

Template Method Pattern

일반적인 흐름에서는 동일하지만 특정 부분만 다른 동작을 하는 경우,

일반적인 기능은 상위클래스에 템플릿 메서드로 설계, 특정 부분에 대해서는 하위클래스에서 오버라이드

Door Class의 핵심 기능 : Open(), Close()

public void open(){
 //1) 이미 문이 열려있으면 무시

 //2) 문 Close ----> 제조 업체에 따라 달라지는 부분
 //3) 문의 상태를 "닫힘"으로 설정

public abstract class Door{
 private DoorStatus doorStatus;
 
 public Door() { doorStatus = DoorStatus.CLOSED; }
 public DoorStatus getDoorStatus() { return doorStatus; }

 protected abstract void doClose();
 public void close(){
     //이미 문이 닫혀있으면 return
     if(doorStatus == DoorStatus.CLOSED){
         return;
     }
     //doClose(): 하위 클래스에서 오버라이드
     doClose();
     doorStatus = DoorStatus.CLOSED;
 }

 protected abstract void doOpen();
 public void open(){
     //이미 문이 열려있으면 return
     if(doorStatus == DoorStatus.OPENED){
         return;
     }
     //doOpen(): 하위 클래스에서 오버라이드
     doOpen();
     doorStatus = DoorStatus.OPENED;
 }
}

LGDoor

public class LGDoor extends Door{
 protected void doClose(){
     System.out.println("close LG Door");
 }
 protected void doOpen(){
     System.out.println("open LG Door");
 }

HyundaiDoor

public class HyundaiDoor extends Dooe{
 protected void doClose(){
     System.out.println("close Hyundai Door");
 }
 protected void doOpen(){
     System.out.println("open Hyundai Door");
 }

 

+ 객체 생성 처리를 서브 클래스로 분리하여 캡슐화함으로써 객체 생성 변화에 대비할 수 있다.

 

public enm VendorID { LG, HYUNDAI }
//객체 생성
public class MotorFactory {
    public static Motor createMotor(VendorID vendorID){
    //객체(VendorID)에 따라서 다른 motor값을 가지게 한다.
        Motor motor = null;

        switch(venderID){
            case LG:
                motor = new LGMotor();
                break;
            case HYUNDAI:
                motor = new HyundaiMotor();
                break;
            }
            return motor;
         }
    }
public class DoorFactory {
    public static Door createDoor(VendorID vendorID){
    //객체(VendorID)에 따라서 다른 door1206값을 가지게 한다.
        Motor motor = null;

        switch(venderID){
            case LG:
                door = new LGMotor();
                break;
            case HYUNDAI:
                door = new HyundaiDoor();
                break;
            }
            return door;
         }
    }
public class Client{
    public static void main(String[] args){
        //팩토리 메서드(MotorFactory/DoorFactory의 createMotor/createDoor 메소드) 호출
        Motor lgMotor = MotorFactory.createMotor(VendorID.LG);
        Door lgDoor = DoorFactory.createDoor(VendorID.LG);      
        
        lgMotor.setDoor(lgDoor);
        lgDoor.open();
        lgMotor.move(Direction.up);
    }
}

 

문제점

  • 다른 제조 업체의 부품을 사용해야 하는 경우
public class Client{
    public static void main(String[] args){
        Door hyundaiDoor = DoorFactory.createDoor(vendorID.HYUNDAI);
        Door hyundaiDoor = DoorFactory.createDoor(VendorID.HYUNDAI);     

        hyundaiMotor.setDoor(hyundaiDoor);
        hyundaiDoor.open();
        hyundaiMotor.move(Direction.up);
    }
}

만약 총 10개의 부품을 사용해야 한다면 각 Factory 클래스를 구현하고, 이들의 Factory 객체를 각각 생성해야하는 번거로움이 있다.

부품의 수가 많아질수록 코드의 길이는 길어지고, 복잡해진다.

 

  • 새로운 제조 업체의 부품을 지원해야 하는 경우
public class MotorFactory {
    public static Motor createMotor(VendorID vendorID){
        Motor motor = null;

        switch(venderID){
            case LG:
                motor = new LGMotor();
                break;

            case HYUNDAI:
                motor = new HyundaiMotor();
                break;

            case SAMSUNG:
                motor = new SamsungMotor();
                break;
            }
            return motor;
         }
    }

MotorFactory뿐만 아니라 DoorFactory 등등 부품과 연관된 Factory클래스에서도 SAMSUNG의 부품을 생성하도록 추가

기존의 팩토리 메서드 패턴을 이용한 객체 생성은 관련있는 여러 개의 객체를 일관성 있는 방식으로 생성하는 경우에 많은 코드 변경이 발생한다.

 

해결책

여러 종류의 객체를 생성할 때 객체들 사이의 관련성이 있는 경우, 각 부품별로 별도의 Factory 클래스를 사용하는 대신 관련 객체들을 일관성있게 생성하는 Factory 클래스를 사용

 

EX) MotorFactory, DoorFactory 클래스처럼 부품 별로 Factory Class를 만드는 대신,

LGElevatorFactory, HyundaiFactory 클래스처럼 제조업체 별로 Factory Class를 만든다.

 

 

  • 부품을 생성하는 추상 팩토리 클래스
public abstract class ElevatorFactory{
    public abstract Motor createMotor();
    public abstract Door createDoor();
}
  •  제조 업체별 부품 생성 팩토리 클래스
//LG
public class LGElevatorFactory extends ElevatorFactory{
    public Motor createMotor() { return new LGMotor(); }
    public Door createDoor() { return new LGDoor(); }
}
//Hyundai
public class HyundaiElevatorFactory extends ElevatorFactory{
    public Motor createMotor() { return new HyundaiMotor(); }
    public Door createDoor() { return new Hyundai(); }
}
public class Client {
    public static void main(String[] args){
        ElevatorFactory factory = null;
        String vendorName = args[0];

        //vendorName에 따라 LG 또는 Hyundai 팩토리 생성
        if(vendorName.equalsIgnoreCase("LG")){   
           factory = new LGElevatorFactory();        
        else                                                  
            factory = new HyundaiElevatorFactory();

         Door door = factory.createDoor();
         Motor motor = factory.createMotor();

         motor.setDoor(door);
         door.open();
         motor.move(Direction.UP);
         }
    }
  • 업체에 따라 적절한 부품 객체를 생성하게 되면, 다른 제조 업체의 부품으로 변경하는 경우에도 Client코드를 변경할 필요가 없다.
  • 제조 업체별로 팩토리 클래스를 정의했으므로 제조 업체별 부품 객체를 간단히 생성할 수있다. 따라서 새로운 제조업체의 부품을 지원하는 경우에도 해당 제조 업체의 부품을 생성하는 팩토리 클래스만 만들면 된다.

 

 

 

 

//SAMSUNG 부품을 생성하는 팩토리 클래스
public class SamsungElevatorFactory extends ElevatorFactory{
    public Motor createMotor() { return new SamsungMotor(); }
    public Door createDoor() { return new SamsungDoor(); }
}

//SAMSUNG Door 클래스
public class SamsungDoor extends Door{
    protected void doClose() { System.out.println("close Samsung Door"); }
    protected void doOpen() { Systme.out.println("open Samsung Door"); }
}

//SAMSUNG Motor 클래스
public class SamsungMotor extends Motor{
    protected void doUp() { System.out.println("Elevator UP"); }
    protected void doDown() { Systme.out.println("Elevator DOWN"); }
}
public class Client{
    public static void main(Strings[] args){
        EevatorFactory factory = null;
        String vendorName = args[0];

        //인자에 따라 LG 또는 Samsung  또는 Hyundai 팩토리 생성
        if(vendorName.equalsIgnorCase("LG")){
            factory = new LGElevatorFactory();
         }
         else if(vendorName.equalsIgnoreCase("Samsung")){
            factory = new SamsungElevatorFactory();
          }
          else
            factory = new HyundaiElevatorFactory();
    }
}

'2021 > JOB DA STUDY' 카테고리의 다른 글

VMware Network  (0) 2021.01.12
SSH  (0) 2021.01.11
디자인패턴_Strategy Pattern of Behavioral Pattern  (0) 2021.01.06
디자인패턴_Singleton Pattern of Creational Pattern  (0) 2021.01.05
Final  (0) 2021.01.05