팩토리 패턴은 생성 패턴 중 하나이다
팩토리 패턴은 심플 팩토리 패턴, 팩토리 메서드 패턴, 추상 팩토리 패턴으로 구분할 수 있다
이 순서대로 하나씩 문제와 문제점을 해결해가며 이해해보도록 하자
1) 심플 팩토리 패턴
우선 제일 기본 팩토리 패턴이다
엘레베이터 추상 클래스가 있고 엘레베이터를 만드는 기업 LG, 현대가 있다고 하자
public abstract class Elevator {
int floor;
public Elevator(int floor) {
this.floor = floor;
}
public void go(int to) {
System.out.println("엘베가 " + to + "로 이동");
floor = to;
}
}
public class LGElevator extends Elevator {
public LGElevator(int floor) {
super(floor);
}
@Override
public void go(int to) {
System.out.println("엘지 엘베가 " + to + "로 이동");
this.floor = to;
}
}
public class HyundaiElevator extends Elevator {
public HyundaiElevator(int floor) {
super(floor);
}
@Override
public void go(int to) {
System.out.println("현대 엘베가 " + to + "로 이동");
this.floor = to;
}
}
이렇게 엘리베이터 추상클래스가 있고 추상클래스를 상속한 엘지와 현대 엘베가 있다
public class Run {
public static void main(String[] args) {
Elevator lgElevator = new LGElevator(1);
lgElevator.go(3);
HyundaiElevator hyundaiElevator = new HyundaiElevator(1);
hyundaiElevator.go(10);
}
}
위 Run 코드 처럼 엘지와 현대 엘베를 생성하고 각 층수로 이동시키는 코드이다
여기서 엘레베이터 생성자의 파라미터가 direction 이 추가된다고 하면 어떻게 될까?
Run 쪽에서 모든 new 생성자 코드에 변경이 생긴다
클라이언트(Run클래스)가 객체 생성에 의존하고 있기 때문에 객체에 변경이 생기면 클라이언트도 변경이 필요하게 된다
이때 객체의 생성을 담당하는 심플 팩토리 즉 팩토리 클래스를 만들어 위 문제를 해결할 수 있다
public class ElevatorFactory {
private static Elevator elevator;
public static Elevator createElevator(String name) {
if (name.equals("lg")) {
elevator = new LGElevator(1);
} else if(name.equals("hd")){
elevator = new HyundaiElevator(1);
}
return elevator;
}
}
Elevator lgElevator = ElevatorFactory.createElevator("lg");
lgElevator.go(3);
Elevator hyundaiElevator = ElevatorFactory.createElevator("hd");
hyundaiElevator.go(10);
이렇게 되면 객체 생성의 변경이 발생해도 다른 사용 로직부분이 아닌 factory 클래스 부분만 수정해주면 된다
객체 생성에 대해 사이에 하나의 생성 클래스를 두고 유연한 결합을 하는 것이다
하지만 이 심플 팩토리엔 또 어떤 문제가 있을까?
if - else if 문이 보인다 이는 새로운 엘레베이터 업체가 추가될때마다 코드 수정이 필요할 것만 같다
이 문제를 해결하기 위해 팩토리 메서드 패턴이 등장한다
다시 한번 문제를 짚고 넘어가면 추가되는 엘리베이터가 생기면 코드 수정이 발생하는 문제다
팩토리 메서드 패턴으로 해결해보자
public abstract class ElevatorFactory {
private static Elevator elevator;
public Elevator getElevator() {
elevator = createElevator();
return elevator;
}
public abstract Elevator createElevator();
}
public class LGElevatorFactory extends ElevatorFactory {
@Override
public Elevator createElevator() {
return new LGElevator(1);
}
}
public class HyundaiElevatorFactory extends ElevatorFactory {
@Override
public Elevator createElevator() {
return new HyundaiElevator(1);
}
}
public static void main(String[] args) {
LGElevatorFactory lgElevatorFactory = new LGElevatorFactory();
HyundaiElevatorFactory hyundaiElevatorFactory = new HyundaiElevatorFactory();
Elevator lgElevator = lgElevatorFactory.getElevator();
Elevator hyundaiElevator = hyundaiElevatorFactory.getElevator();
lgElevator.go(3);
hyundaiElevator.go(10);
}
ElevatorFactory라는 추상클래스를 만들었다. 이 추상클래스를 상속해서 LG엘베팩토리, 현대엘베팩토리 등 특정 업체 엘베를 생성해주는 팩토리 클래스를 찍어낼 수 있다. Main 함수에서 Lg와 현대 엘베를 생성해주는 팩토리 클래스를 만들고 그 팩토리 클래스를 통해 각각 엘베를 생성해주는 모습이다. 이렇게 구현하면 카카오라는 업체가 추가되었을때 카카오엘베와 카카오엘베팩토리를 구현하여 카카오엘베를 생성할 수 있다. 이때 수정되는 코드는 없고 추가되는 코드만 발생한다. 수정에는 닫혀있고 확장에는 열려있는 모습이다.
팩토리 메서드 패턴에도 특정 상황이 발생하면 문제가 생길 수 있다. 만약 lg와 현대가 엘리베이터와 에스컬레이터도 같이 시작했다고 하자.
그렇다면 lg에스컬레이터팩토리, 현대에스컬레이터팩토리와 lg에스컬레이터, 현대에스컬레이터를 추가하면 될 것이다. 하지만 이런 식으로 계속 사업을 확장해 나갔을때 lg 모델만 사용한다고 하면 main함수 로직에서 Lg관련 팩토리 클래스를 마구마구 생성해야한다. 이때 현대로 변경하고 싶을때 만약 100개의 LG 팩토리 클래스가 있었다면 모두 수정을 해야한다.
이렇게 생성이 필요한 상황에서 비슷한 계열로 묶일 수 있을때 추상 팩토리 패턴을 사용할 수 있다.
public static void main(String[] args) {
LGElevatorFactory lgElevatorFactory = new LGElevatorFactory();
LGEscalatorFactory lgEscalatorFactory = new LGEscalatorFactory();
HyundaiElevatorFactory hyundaiElevatorFactory = new HyundaiElevatorFactory();
HyundaiEscalatorFactory hyundaiEscalatorFactory = new HyundaiEscalatorFactory();
Elevator lgElevator = lgElevatorFactory.getElevator();
Elevator hyundaiElevator = hyundaiElevatorFactory.getElevator();
Escalator lgEscalator = lgEscalatorFactory.createEscalator();
Escalator hyundaiEscalator = hyundaiEscalatorFactory.createEscalator();
lgElevator.go(3);
hyundaiElevator.go(10);
lgEscalator.go(10);
hyundaiEscalator.go(4);
}
위 팩토리 메서드 패턴으로 엘지와 현대 에스컬레이터와 에스컬레이터 팩토리 클래스를 만들어 위 main 함수를 만들었다.
public static void main(String[] args) {
LGElevatorFactory lgElevatorFactory = new LGElevatorFactory();
LGEscalatorFactory lgEscalatorFactory = new LGEscalatorFactory();
Elevator lgElevator = lgElevatorFactory.getElevator();
Escalator lgEscalator = lgEscalatorFactory.createEscalator();
lgElevator.go(3);
lgEscalator.go(10);
}
여기서 고객이 엘지만 선택하여 엘지 제품만 선별하였다. 근데 고객이 변심하여 현대로 변경을 원할때 위 main 코드를 전부 현대 코드로 변경하여야 한다. 지금은 2개뿐이지만 10개 100개라면 고된 작업이 될 것 이다. 이렇게 엘지나 현대로 비슷한 계열로 생성할 수 있다면 위 상황을 편하게 넘길 수 있다.
public interface EnterpriseFactory {
Elevator createElevator();
Escalator createEscalator();
}
public class LgFactory implements EnterpriseFactory {
@Override
public Elevator createElevator() {
return new LGElevator(1);
}
@Override
public Escalator createEscalator() {
return new LGEscalator(1);
}
}
public class HyundaiFactory implements EnterpriseFactory {
@Override
public Elevator createElevator() {
return new HyundaiElevator(1);
}
@Override
public Escalator createEscalator() {
return new HyundaiEscalator(1);
}
}
public static void main(String[] args) {
EnterpriseFactory enterpriseFactory = new LgFactory();
Elevator elevator = enterpriseFactory.createElevator();
Escalator escalator = enterpriseFactory.createEscalator();
elevator.go(10);
escalator.go(20);
}
main 함수에서 lgFactory()로 lg를 선택하였다. 현대로 변경하고 싶으면 위 부분만 수정하면 된다. (사실 이부분도 입력에 따라 if-else로 나누면 수정 필요없다.) 이렇게 lg는 lg로 현대는 현대로 묶어 생성하여 더 유연한 코드를 만들 수 있다.
추가적으로 lg와 현대 뿐만 아니라 다른 기업들도 만들어 if-else로 조건문을 붙여 수정없는 코드를 만들고자 하면 또 main 함수가 더러워질 수 있다. 이때도 마찬가지로 팩토리 메서드 패턴을 사용하여 코드를 더 유연하게 할 수 있다. 추가적으로 생성되는 클래스가 단 하나임을 보장하기 위한 싱글턴 패턴도 곁들여 활용할 수 있다. 이는 추가적으로 진행해보길 바란다.
'리팩토링 > 디자인패턴' 카테고리의 다른 글
| [디자인패턴] 퍼사드 패턴 (1) | 2022.11.26 |
|---|---|
| [디자인패턴] 어댑터 패턴 (1) | 2022.11.14 |
| [디자인패턴] 싱글턴 패턴 (2) | 2022.10.24 |
| [디자인패턴] 옵저버 패턴 (4) | 2022.10.14 |
| [디자인패턴] 전략 패턴 (6) | 2022.10.10 |