JUINTINATION
팩토리 메서드(Factory Method) 패턴 본문
반응형
팩토 메서드 패턴이란?
상황에 따라 객체를 다르게 생성해야 할 때 1개의 클래스가 아닌 코드를 각각의 하위 클래스에서 재정의하게 하여 객체의 생성 코드를 별도의 클래스/메소드로 분리하는 패턴이다.
다음과 같이 엘레베이터 제어 시스템에서 다양한 엘레베이터 스케줄링을 지원한다고 하자.
import java.util.ArrayList;
import java.util.List;
enum Direction {
UP,
DOWN;
}
class ElevatorManager {
private List<ElevatorController> controllers;
private ThroughputScheduler scheduler;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
scheduler = new ThroughputScheduler();
}
public void requestElevator(int destination, Direction direction) {
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
}
class ElevatorController {
private int id;
private int curFloor;
public ElevatorController(int id) {
this.id = id;
curFloor = 1;
}
public void gotoFloor(int destination) {
System.out.println("Elevator [" + id + "] Floor." + curFloor + " ==> " + destination);
curFloor = destination;
}
}
class ThroughputScheduler {
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
- 위 코드의 문제점
- ThroughputScheduler가 아닌 스케줄링 전략을 사용해야 한다면 기존 코드를 수정해야 한다.
- 유지 보수의 어려움
- OCP 위반
- ThroughputScheduler가 아닌 스케줄링 전략을 사용해야 한다면 기존 코드를 수정해야 한다.
위 문제를 해결하기 위해 스트래티지 패턴을 적용해보았다.
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
class ThroughputScheduler implements ElevatorScheduler {
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ResponseTimeScheduler implements ElevatorScheduler {
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ElevatorManager {
private List<ElevatorController> controllers;
private ElevatorScheduler scheduler;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
scheduler = new ThroughputScheduler();
}
public void requestElevator(int destination, Direction direction) {
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
public void setScheduler(ElevatorScheduler scheduler) {
this.scheduler = scheduler;
}
}
- 위 코드의 문제점
- 오전/오후에 따라 스케줄링 방식을 프로그램 실행 중에 바꿔야 한다면 기존 코드를 수정해야 한다.
예를 들어 다음과 같이 오전/오후에 따라 스케줄링 방식을 바꾸도록 코드를 수정하면 문제가 발생한다.
class ElevatorManager {
private List<ElevatorController> controllers;
private ElevatorScheduler scheduler;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
}
public void requestElevator(int destination, Direction direction) {
// 엘레베이터 스케줄링 전략의 추가 및 동적 선택 방식 변경이 필요하면 스케줄링 클래스를 직접 생성하도록 기존 코드를 수정해야 한다.
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour < 12) {
setScheduler(new ResponseTimeScheduler());
} else {
setScheduler(new ThroughputScheduler());
}
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
public void setScheduler(ElevatorScheduler scheduler) {
this.scheduler = scheduler;
}
}
이를 해결하기 위해 스케줄링 전략에 맞는 객체를 생성하는 코드를 별도로 정의한다.
enum SchedulingStrategyID {
THROUGHPUT,
RESPONSE_TIME,
DYNAMIC
}
class SchedulerFactory {
public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
ElevatorScheduler scheduler = null;
switch (strategyID) {
case THROUGHPUT:
scheduler = new ThroughputScheduler();
break;
case RESPONSE_TIME:
scheduler = new ResponseTimeScheduler();
break;
case DYNAMIC:
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour < 12) {
scheduler = new ResponseTimeScheduler();
} else {
scheduler = new ThroughputScheduler();
}
break;
}
return scheduler;
}
}
class ElevatorManager {
private List<ElevatorController> controllers;
private SchedulingStrategyID strategyID;
public ElevatorManager(int controllerCount, SchedulingStrategyID strategyID) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
setStrategyID(strategyID);
}
public void requestElevator(int destination, Direction direction) {
ElevatorScheduler scheduler = SchedulerFactory.getScheduler(strategyID);
System.out.println(scheduler);
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
public void setStrategyID(SchedulingStrategyID strategyID) {
this.strategyID = strategyID;
}
}
전체 코드를 보면 다음과 같다.
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
enum Direction {
UP,
DOWN;
}
enum SchedulingStrategyID {
THROUGHPUT,
RESPONSE_TIME,
DYNAMIC
}
class SchedulerFactory {
public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
ElevatorScheduler scheduler = null;
switch (strategyID) {
case THROUGHPUT:
scheduler = new ThroughputScheduler();
break;
case RESPONSE_TIME:
scheduler = new ResponseTimeScheduler();
break;
case DYNAMIC:
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour < 12) {
scheduler = new ResponseTimeScheduler();
} else {
scheduler = new ThroughputScheduler();
}
break;
}
return scheduler;
}
}
interface ElevatorScheduler {
int selectElevator(ElevatorManager manager, int destination, Direction direction);
}
class ThroughputScheduler implements ElevatorScheduler {
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ResponseTimeScheduler implements ElevatorScheduler {
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ElevatorManager {
private List<ElevatorController> controllers;
private SchedulingStrategyID strategyID;
public ElevatorManager(int controllerCount, SchedulingStrategyID strategyID) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
setStrategyID(strategyID);
}
public void requestElevator(int destination, Direction direction) {
ElevatorScheduler scheduler = SchedulerFactory.getScheduler(strategyID);
System.out.println(scheduler);
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
public void setStrategyID(SchedulingStrategyID strategyID) {
this.strategyID = strategyID;
}
}
class ElevatorController {
private int id;
private int curFloor;
public ElevatorController(int id) {
this.id = id;
curFloor = 1;
}
public void gotoFloor(int destination) {
System.out.println("Elevator [" + id + "] Floor." + curFloor + " ==> " + destination);
curFloor = destination;
}
}
또한 스케줄링 전략을 바꿀 때마다 새로운 객체를 생성할 필요가 없으니 싱글턴 패턴까지 적용하면 다음과 같다.
class SchedulerFactory {
public static ElevatorScheduler getScheduler(SchedulingStrategyID strategyID) {
ElevatorScheduler scheduler = null;
switch (strategyID) {
case THROUGHPUT:
scheduler = ThroughputScheduler.getInstance();
break;
case RESPONSE_TIME:
scheduler = ResponseTimeScheduler.getInstance();
break;
case DYNAMIC:
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour < 12) {
scheduler = ResponseTimeScheduler.getInstance();
} else {
scheduler = ThroughputScheduler.getInstance();
}
break;
}
return scheduler;
}
}
interface ElevatorScheduler {
int selectElevator(ElevatorManager manager, int destination, Direction direction);
}
class ThroughputScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler = new ThroughputScheduler();
private ThroughputScheduler() {};
public static ElevatorScheduler getInstance() {
return scheduler;
}
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ResponseTimeScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler = new ResponseTimeScheduler();
private ResponseTimeScheduler() {};
public static ElevatorScheduler getInstance() {
return scheduler;
}
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
다음은 상속을 이용한 팩토리 메서드 패턴의 적용이다.
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
enum Direction {
UP,
DOWN;
}
enum SchedulingStrategyID {
THROUGHPUT,
RESPONSE_TIME,
DYNAMIC
}
interface ElevatorScheduler {
int selectElevator(ElevatorManager manager, int destination, Direction direction);
}
class ThroughputScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler = new ThroughputScheduler();
private ThroughputScheduler() {};
public static ElevatorScheduler getInstance() {
return scheduler;
}
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
class ResponseTimeScheduler implements ElevatorScheduler {
private static ElevatorScheduler scheduler = new ResponseTimeScheduler();
private ResponseTimeScheduler() {};
public static ElevatorScheduler getInstance() {
return scheduler;
}
@Override
public int selectElevator(ElevatorManager manager, int destination, Direction direction) {
return 0; // 임의로 선택한다.
}
}
abstract class ElevatorManager {
private List<ElevatorController> controllers;
private SchedulingStrategyID strategyID;
public ElevatorManager(int controllerCount) {
controllers = new ArrayList<>(controllerCount);
for (int i = 0; i < controllerCount; i++) {
controllers.add(new ElevatorController(i + 1));
}
}
protected abstract ElevatorScheduler getScheduler(); // primitive 메서드
public void requestElevator(int destination, Direction direction) { // 템플릿 메서드
ElevatorScheduler scheduler = getScheduler();
System.out.println(scheduler);
int selectedElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectedElevator).gotoFloor(destination);
}
}
class ElevatorManagerWithThroughputScheduler extends ElevatorManager {
public ElevatorManagerWithThroughputScheduler(int controllerCount) {
super(controllerCount);
}
@Override
protected ElevatorScheduler getScheduler() {
return ThroughputScheduler.getInstance();
}
}
class ElevatorManagerWithResponseTimeScheduler extends ElevatorManager {
public ElevatorManagerWithResponseTimeScheduler(int controllerCount) {
super(controllerCount);
}
@Override
protected ElevatorScheduler getScheduler() {
return ResponseTimeScheduler.getInstance();
}
}
class ElevatorManagerWithDynamicScheduler extends ElevatorManager {
public ElevatorManagerWithDynamicScheduler(int controllerCount) {
super(controllerCount);
}
@Override
protected ElevatorScheduler getScheduler() {int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if (hour < 12) {
return ResponseTimeScheduler.getInstance();
} else {
return ThroughputScheduler.getInstance();
}
}
}
class ElevatorController {
private int id;
private int curFloor;
public ElevatorController(int id) {
this.id = id;
curFloor = 1;
}
public void gotoFloor(int destination) {
System.out.println("Elevator [" + id + "] Floor." + curFloor + " ==> " + destination);
curFloor = destination;
}
}
팩토리 메서드 패턴 컬레보레이션
Product : 팩토리 메서드로 생성될 객체의 공통 인터페이스
ConcreteProduct : 구체적으로 객체가 생성되는 Product 하위 클래스
Creator : 팩토리 메서드를 갖는 추상 클래스
ConcreteCreator : 팩토레 메서드를 구현하는 ConcreteProduct 객체를 생성하는 Creator 하위 클래스
728x90
'JAVA객체지향디자인패턴' 카테고리의 다른 글
컴퍼지트(Composite) 패턴 (0) | 2023.07.17 |
---|---|
추상 팩토리(Abstract Factory) 패턴 (0) | 2023.07.16 |
템플릿 메서드(Template Method) 패턴 (0) | 2023.07.16 |
데커레이터(Decorator) 패턴 (0) | 2023.07.09 |
옵서버(Observer) 패턴 (0) | 2023.07.06 |
Comments