엘리베이터를 구성하는 부품 중에 모터와 문을 생각해보자. 예시로 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;
}
}
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 클래스를 정의했으므로 부품 객체를 간단하게 생성할 수 있다.
추상 팩토리 패턴은 관련성 있는 여러 종류의 객체를 일관된 방식으로 생성하는 경우에 유용하다. 부품별로 Factory를 정의하는 대신 관련 객체들을 일관성 있게 생성할 수 있도록 Factory 클래스를 정의하는 것이 효과적이다. MotorFactory, DoorFactory 클래스를 정의하는 것 대신 LG 부품들을 위한 LGFactory, 현대 부품들을 위한 HyundaiFactory를 정의하는 것이 바람직하다.
실제 팩토리 클래스의 공통 인터페이스이다. 각 제품의 부품을 생성하는 기능을 추상 메서드로 정의한다.
구체적인 팩토리 클래스로 AbstractFactory 클래스의 추상 메서드를 오버라이드함으로써 구체적인 제품을 생성한다.
제품의 공통 인터페이스
구체적인 팩토리 클래스에서 생성되는 구체적인 제품