Design pattern

[Design Pattern] Abstract Factory Pattern

s00ng 2022. 8. 22. 18:19
추상 팩토리 패턴이란?
서로 관련이 있는 객체들을 통째로 묶어서 팩토리 클래스로 만들고, 이들 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴
관련성있는 여러 종류의 객체를 일관성 있는 방식으로 생성할 때 유용

 

 

  • AbstractFactory: 실제 팩토리 클래스의 공통 인터페이스. 각 제품의 부품을 생성하는 기능을 추상 메서드로 정의
  • ConcreteFactory: 구체적인 팩토리 클래스로 AbstractFactory 클래스의 추상 메서드를 오버라이드 함으로써 구체적인 제품 생성
  • AbstractProduct: 제품의 공통 인터페이스
  • ConcreteProduct: 구체적인 팩토리 클래스에서 생성되는 구체적인 제품

 

추상 팩토리 패턴 구현

 

교재 예시 : 엘리베이터 부품 업체 변경
  • 엘리베이터 부품중 모터와 문
  • 추상클래스로 Motor를 정의, LGMotor 와 HyundaiMotor를 하위 클래스
  • 추상클래스로 Door 정의, LGDoor 와 HyundaiDoor를 하위 클래스
  • 모터 구동 단계: 이미 이동중이면 무시 → 문이 열려있으면 문 닫기 → 모터를 구동해서 이동 → 모터의 상태를 이동중으로 설정
public abstract class Door {
	private DoorStatus doorStatus;
    
    public DoorStatus getDoorStatus(){
    	return doorStatus;
    }
    
	public void close() { //템플릿 메서드
    	if(doorStatus == DoorSatus.CLOSED)
        	return;
        doClose(); //실제로 문 닫는 동작 수행 (하위 클래스에서 오버라이드)
        doorStatus = DoorStatus.CLOSED;
    	
    }
    
    protected abstract void doClose();
    
    public void open(){
    
    	if(doorStatus == DoorStatus.OPENED)
        	return;
        
        doOpen();  //실제로 문 여는 동작 수행 (하위 클래스에서 오버라이드)
        doorStatus = DoorStatus.OPENED;
    }
    protected abstract void doOpen();
}
public class LGDoor extends Door {
	protected void doClose(){
    	System.out.println("close LG Door");
    }
    protected void doOpen(){
    	System.out.println("open LG Door");
    }
}

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

public enum VendorId {LG, HYUNDAI}

public class MotorFactory { //팩토리 메서드 패턴 사용
	public static Motor createMotor(VendorId vendorID){
    	Motor motor = null;
        switch (vendorID){
        	case LG:
            	motor = new LGMotor();
                break;
        	case HYUNDAI:
            	motor = new HyundaiMotor();
                break;
        }	
    	return motor;
    }
}

 

문제점

  • 만약 다른 제조 업체의 부품을 사용해야한다면?
  • 새로운 제조 업체의 부품을 지원해야한다면?
팩토리 메소드 패턴을 이용한 객체 생성은 관련있는 여러 개의 객체를 일관성 있는 방식으로 생성하는 경우, 많은 코드 변경이 발생

 

해결책

추상 팩토리 패턴 구현 : 여러 종류의 객체를 생성할 때, 객체들 사이에 일관성이 있는 경우, 관련 객체들을 일관성 있게 생성하는 Factory 클래스를 사용
  • ex) 엘리베이터 프로그램의 경우, MotorFactory, DoorFactory 클래스와 같이 부품별로 Factory 클래스를 만드는 대신, LGElevatorFactory 나 HyundaiElevatorFactory 클래스와 같이 제조 업체별로 Factory 클래스 만들 수 있음.
public abstract class ElevatorFactory {
	public abstract Motor createMotor();
    public abstract Door createDoor();

}

public class LGElevatorFactory extends ElevatorFactory { // LG 부품을 생성하는 팩토리
	public Motor createMotor(){
    	return new LGMotor();
    }
	public Door createDoor(){
    	return new LGDoor();
    }
}

public class HyundaiElevatorFactory extends ElevatorFactory { // 현대 부품을 생성하는 팩토리
	public Motor createMotor(){
    	return new HyundaiMotor();
    }
	public Door createDoor(){
    	return new HyundaiDoor();
    }
}

public class ElevatorFactoryFactory { // 팩토리 클래스에 팩토리 메서드 패턴을 적용
	public static ElevatorFactory getFactory(VendorID vendorID){
    	ElevatorFactory factory = null;
        switch(vendorID){
        	case LG:
            	factory = LGElevatorFactory.getInstance();
                break;
            case HYUNDAI:
            	factory = HyundaiElevatorFactory.getInstance();
                break;
           case SAMSUNG:
           		factory = SaunsungElevatorFactory.getInstance();
                break;
        }	
    	return factory;
    }
}

public class LGElevatorFactory extends ElevatorFactory { // 싱글턴 패턴 적용한 LG 팩토리
	private static ElevatorFactory factory;
    private LGElevatorFactory(){}
    
    public static ElevatorFactory getInstance(){
    	if(factory == null)
        	factory = new LGElevatorFactory();
        return factory;
    }
    public Motor createMotor(){
    	return new LGMotor();
    }
   	public Door createDoor(){
    	return new LGDoor();
    }
}

public class Client {

	public static void main(String args[]){
    	String vendorName = args[0];
        VendorId vendorID;
        if (vendorName.equalsIgnoreCase("LG"))
        	vendorID = VendorID.LG;
        else if (vendorName.equalsIsIgnoreCase("Sansung"))
        	vendorID = VendorID.SANSUNG;
    	else
        	vendorID = VendorID.HYUNDAI;
            
       ElevatorFactory factory = ElevatorFactoryFactory.getFactory(vendorID);
       
       Door door = factory.createDoor();
       Motor motor = factory.createMotor();
       motor.setDoor(door);
       
       door.open();
       motor.move(Direction.UP);
    
    }
}
Client 객체는 ConcreteFactory의 변화에 의해 영향을 받지 않을 수 있다