추상 팩토리 패턴

이연희·2022년 8월 1일
0

Design_Pattern

목록 보기
10/11

Chap13 추상 팩토리 패턴

엘리베이터 부품 업체 변경하기

엘리베이터를 구성하는 부품 중에 모터와 문을 생각해보자. 예시로 LG는 LG 모터와 LG 문을 제공하고 현대는 현대모터와 현대 문을 제공한다고 하자. 두 종류의 모터와 문은 구체적인 제어 방식은 다르지만 엘리베이터 입장에서는 기능적인 부분에선 동일하다. 그러므로 추상 클래스로 Motor를 정의하고 하위 클래스로 LGMotor, HyundaiMotor를 하위 클래스로 정의할 수 있다. LGDoor, HyundaiDoor를 Door 클래스의 하위 클래스로 정의할 수 있다.

Motor의 핵심 기능인 이동은 move() 메서드로 정의한다.

public void move(Direction direction){
	//1. 이미 이동 중이면 무시
    //2. 만약 문이 열려 있으면 닫음
    //3. 모터를 구동해서 이동
    //4. 모터 상태를 이동 중으로 설정
}	

이러한 4단계의 move 동작은 LGMotor, HyundaiMotor 클래스 모두 동일하다. 하지만 "3.모터를 구동해서 이동" 부분만 달라진다. 일반적인 흐름을 동일하지만 특정 부분만 다른 동작을 하는 경우에 일반적인 기능을 상위 클래스에 템플릿 메서드로 설계할 수 있다.

Door 클래스에서도 open과 close 메서드 각각에 템플릿 메서드 패턴을 적용할 수 있다. 그리고 문을 닫는 부분만 하위 클래스에서 구현하도록 한다.

public abstract class Door{
	private DoorStatus doorStatus;
    publi Door(){
    	doorStatus=DoorStatus.CLOSED;
    }
    
    public void close(){//템플릿 메서드 
    	if(doorStatus==DoorStatus.CLOSED)
        	return;
        
        doClose();//하위 클래스에서 오버라이드
        doorStatus=DoorStatus.CLOSED;
    }
    protected abstract void doClose();//hook or primitive method
    
    public void open(){//템플릿 메서드
    	if(doorStatus==DoorStatus.OPENED)
        	return;
        
        doOpen();//하위 클래스에서 오버라이드
        doorStatus=DoorStatus.OPENED;
    }
    protected abstract void doOpen();//hook or primitive method
}

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{
	...
}

엘리베이터 입장에서 모터와 문을 제어하는 클래스가 필요하다. 이 경우에도 팩토리 메서드 패턴을 적용할 수 있따. 즉, MotorFactory 클래스를 정의해서 LGMotor와 HyundaiMotor 중에서 특정 제조 업체에 따라 해당 Motor 객체를 생성할 수 있다.

public enum VenderId{LG, HYUNDAI}

public class MotorFactory{
	public static Motor createMotr(VenderID venderID){
    	Motor motor = null;
        switch(venderID){
        	case LG:
              motor=new LGMotor();
              break;
            case HYUNDAI:
              motor=new HyundaiMotor();
              break;
		}	
        return motor;
    }
}

문제점

현재 사용하는 LG 대신 현대 부품을 사용해야 한다면?

MotorFactory와 DoorFactory 클래스를 이용해서 이미 정의된 HyundaiMotor 객체와 HyundaiDoor 객체를 생성하도록 프로그램을 수정한다.

public class Client{
	public static void main(String[] args){
    	Door hyundaiDoor = DoorFactory.createDoor(VendorID.HYUNDAI);
        Motor hyundaiMotor=MotorFactory.createMotor(VendorID.YUNDAI);
        hyundaiMotor.setDoor(hyundaiDoor);
        
        hyundaiDoor.open();
        hyundaiMotor.move(Direction.UP);
    }
}

이런 구조라면 다른 부품을 추가할 때마다 각 Factory 클래스를 구현하고 이들의 Factory 객체를 각각 생성해야 한다. 코드의 길이가 길어지고 복잡해질 수 있다. 그리고 코드를 수정하는 것이 쉽지 않다.

새로운 제조 업체인 삼성의 부품을 지원해야 한다면?

만약 삼성 부품을 사용하고 싶으면 MotorFactory와 DoorFactory 클래스가 SamsungMotor와 SamsungDoor 클래스를 지원할 수 있도록 수정해야 한다.

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

해결책

MotorFactory, DoorFactory 클래스와 같이 부품별로 Factory 클래스를 만드는 대신 LGElevatorFactory나 HyundaiElevatorFactory 클래스와 같이 제조 업체별로 Factory 클래스를 만들 수 있다.

public abstract class ElevatorFactory{
	public abstract Motor createMotor();
    public abstract Door createDoor();
}

public class LGElevatorFactory extends ElevatorFactory{
	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();
    }
}

제조 업체별로 Factory 클래스를 정의했으므로 부품 객체를 간단하게 생성할 수 있다.

추상 팩토리 패턴(Abstract Factory Pattern)

추상 팩토리 패턴은 관련성 있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우에 유용하다. 부품별로 Factory를 정의하는 대신 관련 객체들을 일관성 있게 생성할 수 있도록 Factory 클래스를 정의하는 것이 효과적이다. MotorFactory, DoorFactory 클래스를 정의하는 것 대신 LG 부품들을 위한 LGFactory, 현대 부품들을 위한 HyundaiFactory를 정의하는 것이 바람직하다.

AbstractFactory

실제 팩토리 클래스의 공통 인터페이스이다. 각 제품의 부품을 생성하는 기능을 추상 메서드로 정의한다.

ConcreteFactory

구체적인 팩토리 클래스로 AbstractFactory 클래스의 추상 메서드를 오버라이드함으로써 구체적인 제품을 생성한다.

AbstractProduct

제품의 공통 인터페이스

ConcreteProduct

구체적인 팩토리 클래스에서 생성되는 구체적인 제품

  • ElevatorFactory 클래스는 AbstractFactory 역할을 한다.
  • LGElevatorFactory, HyundaiElevatorFactory 클래스는 ConcreteFactory 역할을 한다.
  • Motor 클래스는 AbstractProductA 역할을 한다.
  • LGMotor 클래스와 HyundaiMotor 클래스는 ConcreteProductA 역할을 한다.
  • Door 클래스는 AbstractProductB 역할을 한다.
  • LGDoor 클래스와 HyundaiDoor 클래스는 ConcreteProductB 역할을 한다.
profile
공부기록

0개의 댓글