2021. 1. 19. 14:39ㆍ2021/JOB DA STUDY
객체 생성 처리를 서브 클래스로 분리해 처리하도록 캡슐화하는 패턴
- 객체의 생성 코드를 별도의 클래스와 메서드로 분리해 객체 생성의 변화에 대비 가능
- 특정 기능의 구현은 개별 클래스를 통해 제공되는 것이 바람직한 설계
--> 기능의 변경이나 상황에 따른 기능의 선택은 해당 객체를 생성하는 코드의 변경을 초래
--> 상황에 따라 적절한 객체를 생성하는 코드는 자주 중복 가능
--> 객체 생성 방식의 변화는 해당하는 모든 코드 부분을 변경해야 하는 문제 발생
- Product: Factory Method로 생성될 객체의 공통 인터페이스
- ConcreteProduct: 구체적으로 객체가 생성되는 클래스
- Creator: 팩토리 메서드를 갖는 클래스
- ConcreteCreator: 팩토리 메서드를 구현하는 클래스로 ConcreteProduct 객체를 생성
1. 객체 생성을 전담하는 별도의 Factory 클래스를 이용한다.
- 스트래티지 패턴과 싱글톤 패턴을 이용한다.
2. 상속을 이용: 하위 클래스에서 적합한 클래스의 객체를 생성
- 스트래티지 패턴, 싱글톤 패턴 그리고 템플릿 메서드 패턴을 이용한다.
여러가지 방식의 엘리베이터 스케줄링 방법 지원하기
작업 처리량(Throughput)을 기준으로 한 스케줄링에 따른 엘리베이터 관리
스케줄링: 주어진 요청(목적지 충과 방향 = 엘리베이터 버튼)을 받았을 때 여러대의 엘리베이터 중 하나를 선택하는 것
public class ElevatorManager{
private List<ElevatorController> controllers;
private ThroughputScheduler schduler;
// 주어진 수 (controllerCount) 만큼의 ElevatorController를 진행
public ElevatorManager(int controllerCount){
//엘리베이터의 이동을 책임지는 ElevatorController 객체 생성
controllers = new ArrayList<ElevatorController>(controllerCount);
for(int i=0; i<controllerCount; i++){
ElevatorController controller = new ElevatorController(1);
controllers.add(controller);
}
//엘리베이터를 스케줄링(엘리베이터 선택) 하기 위한 ThroughputSceduler 객체를 생성
scheduler = new ThroughputScheduler();
}
//요청에 따라 엘리베이터 선택 & 이동
void requestElevator(int destination, Direction direction){
//ThroughputScheduler를 이용해 엘리베이터를 선택
int selectedElevator = scheduler.selectElevator(this, destination, direction);
//선택된 엘리베이터를 이동시킴
controllers.get(selectElevator).gotoFlooer(destination);
}
}
- ElevatorManager 클래스
- 이동 요청을 처리하는 클래스
- 엘리베이터를 스케줄링 (엘리베이터 선택) 하기 위한 TroughputScheduler 객체를 갖는다.
- 각 엘리베이터의 이동을 책임지는 ElevatorController 객체를 복수 개 갖는다.
- requestElevator() 메서드
- 요청 (목적지에 대한 층과 방향)을 받았을 때 우선 ThroughputScheduler 클래스의 selectElevator() 메서드를 호출해 적정한 엘리베이터를 선택한다.
- 선택된 엘리베이터에 해당하는 ElevatorController 객체의 gotoFloor() 메서드를 호출해 엘리베이터 이동시킨다.
public class ElevatorController{
private int id; //엘리베이터 ID
private int curFloor; //현재 층
public ElevatorController(int id){
this.id = id;
curFloor = 1;
}
public void gotoFloor(int destinaiton) {
System.out.print("Elevator [" + id + "] Floor:" + curFloor);
//현재 층 갱신 ( 주어진 목적지 층(destination)으로 엘리베이터 이동
curFlooer = destination;
System.out.print("==>" + curFloor);
}
}
public class ThroughputScheduler {
public int selectElevator(ElevatorManager manager, int destination, Direction direction){
return 0; // 임의로 선택함
}
}
public calss ResponseTimeScheduler{
public int selectElevator(ElevatorManager manager, int destination, Direction direction){
return 1; // 임의로 선택함
}
}
문제점_1
- 다른 스케줄링 전략을 사용해야 하는 경우
엘리베이터 작업 처리량을 최대화 (ThroughputScheduler 클래스) 시키는 전략이 아닌, 사용자의 대기 시간을 최소화 하는 엘리베이터 전략을 사용하는 경우
해결책
public class ElevatorManager{
private List<ElevatorController> controllers;
public ElevatorManager(int controllerCount){
//엘리베이터의 이동을 책임지는 ElevatorController 객체를 생성
controllers = new ArrayList<ElevatorController>(controllerCount);
//주어진 수 만큼의 ElevatorController를 생성
for( int i=0; i< controllerCount; i++){
ElevatorController controller = new ElevatorController(i+1);
controllers.add(controller);
}
}
//요청에 따라 엘리베이터를 선택하고 이동
void requestElevator(int destination, Direction direction){
ElevatorScheduler scheduler; //인터페이스
int hour = Calendar.getInstance().get(Calender.HOUR_OF_DAY);
//오전: ResponseTimeScheduler 오후: ThroughputScheduler
if(hour < 12) {
scheduler = new ResponseTimeScheduler();
}
else{
scheduler = new ThroughScheduler();
}
//ElevatorScheduler 인터페이스를 이용해 엘리베이터 선택
int selectElevator = scheduler.selectElevator(this, destination, direction);
controllers.get(selectElevator).gotoFloor(destination);
}
}
1. ElevatorManager의 requestManager() 메서드가 실행될 때 마다, 현재 시간에 따라 적절한 스케줄링 객체를 생성
2. ElevatorManager 클래스의 입장에서는 여러 스케줄링 전략이 있기 때문에 ElevatorScheduler라는 인터페이스를 사용해 여러 전략들을 캡슐화 하고, 동적으로 선택할 수 있게 한다.
문제점_2
- 프로그램 실행 중에 스케줄링 전략을 변경하는 경우 (동적 스케줄링 지원을 해야 하는 경우 또는 스케줄링 전략이 추가되는 경우)
---> 해당 스케줄링 전략을 지원하는 구체적인 클래스를 생성
---> ElevatorManager 클래스의 requestElevator() 메서드(1. 엘리베이터 선택, 2. 엘리베이터 이동) 수정
EX) 새로운 스케줄링 전략의 추가 = 엘리베이터 노후화 최소화 전략
동적 스케줄링 방식의 변경 = 오전: 대기시간 최소화, 오후: 처리량 최대화
해결책1
엘리베이터 스케줄링 전략에 일치하는 클래스를 생성하는 코드를 requestElevator 메서드에서 분리해 별도의 클래스/메서드 정의
변경 전 | 변경 후 |
ElevatorManager클래스가 직접 ThroughputScheduler 객체와 ResponseTimeScheduler 객체를 생성 | SchedulerFactory클래스의 getScheduler()메서드가 전략에 맞는 객체를 생성 |
public enum SchedulingStrategyID { RESPONSE_TIME, THROUGHPUT, DYNAMIC }
public class SchedulerFactory {
//스케줄링 전략에 맞는 객체 생성
public static ElevatorScheduler getScheduler(SchedulingStrategyID starategyID){
swith(strategyID){
//대시시간 최소화 전략
case RESPONSE_TIME:
scheduler = new ResponseTimeScheduler();
break;
//처리량 최대화 전략
case THROUGHPUT:
scheduler = new ThroughputScheduler();
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;
}
}
- ElevatorManager 클래스의 requestElevator()메서드에서는 SchedulerFactory클래스의 getScheduler 메서드를 호출하면 된다.
public class ElevatorManger{
private List<ElevatorController> controllers;
private SchedulingStrategyID strategyID;
//주어진 만큼 ElevatorController를 생성
public ElevatorManager(int controllerCount, SchedulingStrategyID strategyID){
//엘리베이터의 이동을 책임지는 ElevatorController 객체를 생성
controllers = new ArrayList<ElevatorController>(controllerCount);
for(int i=0; i<controllerCount; i++){
ElevatorController controller = new ElevatorController(i+1);
controllers.add(controller);
}
}
//실행 중에 다른 스케줄링 전략으로 지정 가능-세팅
public setStrategyID(SchedulingStrategyID strategyID){
this.strategyID = strategyID;
}
//요청에 따라 엘리베이터를 선택하고 이동시킴
void requestElevator(int destination, Direction direction){
//주어진 전략ID에 해당하는 ElevatorScheduler를 사용(변경된 부분)
ElevatorScheduler scheduler = SchedulerFactory.getScheduler(StrategyID);
System.out.println(scheduler);
//주어진 전략에 따라 엘리베이터를 선택
int selectedElevator = scheduler.selectElevator(this, destination, direction);
//선택된 엘리베이터를 이동시킴
controllers.get(selectedElevator).gotoElevator(destination);
}
}
public class Client{
public static void main(String[] args){
ElevatorManager emWithResponseTimeScheduler = new ElevatorManger(2, SchedulingStrategyID.RESPONSE_TIME);
emWithResponseTimeScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithResponseTimeScheduler = new ElevatorManger(2, SchedulingStrategyID.THROUGHPUT);
emWithResponseTimeScheduler.requestElevator(10, Direction.UP);
ElevatorManager emWithResponseTimeScheduler = new ElevatorManger(2, SchedulingStrategyID.DYNAMIC);
emWithResponseTimeScheduler.requestElevator(10, Direction.UP);
해결책2
동적 스케줄링 방식(Dynamic Schceduler)은 여러번 스케줄링 객체를 생성하지 않고,
한번 생성한 것을 계속해서 사용하는 것
- 싱글톤 패턴을 이용한 엘리베이터 스케줄링 전략 설계
스케줄링 기능을 제공하는 ResponseTimeScheduler 클래스와 ThroughputScheduler 클래스는 오직 하나의 객체만 생성해서 사용하도록 한다.
-> 생성자를 통해 직접 객체를 생성하는 것은 허용되지 않아야 한다.
- 생성자를 Private으로 정의
- getInstance()라는 정적 메서드를 이용해 객체 생성
public class SchedulerFactory{
//스케줄링 전략에 맞는 객체를 생성
public static ElevatorScheduler getScheduler(SchedlingStrategyID strategyID){
ElevatorScheduler scheduler = null;
switch(strateguyID){
//대기시간 최소 전략
case RESPONSE_TIME:
scheduler = ResponseTimeScheduler.getInstance();
break;
//처리량 최대화 전략
case THROUGHPUT:
scheduler = ThroughputScheduler.getInstance();
//동적 스케줄링
case DYNAMIC:
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
if(hour<12){
scheduler = ResponseTimeScheduler.getInstance();
else{
scheduler = ThroughputScheduler.getInstance();
break;
}
return scheduler;
}
}
- 싱글톤 패턴으로 구현한 ThroughputScheduler 클래스
public class ThroughputScheduler{
private static ElevatorScheduler scheduler;
//생성자를 private으로 정의
private ThroughputScheduler(){}
//정적 메소드로 객체 생성을 구현 (싱글톤 패턴)
public static ElevatorScheduler getInstance(){
if(scheduler == null)
scheduler = new ThroughputScheduler();
return scheduler;
}
public int selectElevator(ElecatorManager manager, int destination, Direction direction){
return 0;
}
}
- 싱글톤 패턴으로 구현한 ResponseTimeScheduler 클래스
public class ResponseTimeScheduler{
private static ElevatorScheduler scheduler;
//생성자를 private으로 정의
private ResponseTimeScheduler(){}
//정적 메소드로 객체 생성 구현 (싱글톤 패턴)
public static ElevatorScheduler getInstance(){
if(scheduler == null)
scheduler = new ResponseTimeScheduler();
return scheduler;
}
public int selectElevator(ElevatorManager manager, int destination, Direction direction){
return 1;
}
}
객체 생성을 전담하는 별도의 Factory 클래스를 분리해 객체 생성의 변화에 대비
-> Strategy Pattern과 Singleton Pattern을 이용해 Factory Method Pattern을 적용
- 상속을 이용한 엘리베이터 스케줄링 전략 설계
Strategy Pattern, Singleton Pattern, TemplateMethod Pattern을 이용 -> Factory Method Pattern을 적용
//템플릿 메서드를 정의하는 클래스: 하위 클래스에서 구현되는 기능을 primitive 메서드로 정의
public abstract class ElevatorManager{
private List<ElevatorController> controllers;
public ElevatorManager(int controllerCount){
//엘리베이터의 이동을 책임지는 ElevatorController 객체를 생성
controllers = new ArrayList<ElevatorController>(controllerCount);
for(int i=0; i<controllerCount; i++){
ElevatorController controller = new ElevatorController(i+1);
controllers.add(cntroller);
}
}
//팩토리 메서드: 스케줄링 전략 객체를 생성하는 기능 제공
protected abstract ElevatorScheduler getScheduler();
//템플릿 메서드: 요청에 따라 엘리베이터를 선택하고 이동시킴
void requestElevator(int destination, Direction direction){
//하위 클래스에서 Override된 getScheduler() 메서드를 호출
ElevatorScheduler scheduler = getScheduler(); //primitive 또는 hook 메서드
System.out.println(scheduler);
//주어진 전략에 따라 엘리베이터 선택
int selectedElevator = scheduler.selectElevator(this, destination, direction)
//선택된 엘리베이터를 이동
controllers.get(selectElevator).gotoFlooer(destination);
}
}
- 팩토리 메서드
- ElevatorManager 클래스의 getScheduler() 메서드
- 스케줄링 전략 객체를 생성하는 기능 제공 (객체 생성을 분리)
- 템플릿 메서드
- ElevatorManager 클래스의 requestElevator() 메서드
- 공통 기능( = 스케줄링 전략 객체 생성 / 엘리베이터 선택 / 엘리베이터 이동 )의 일반 로직 제공
- 하위 클래스에서 구체적으로 정의할 필요가 있는 "스케줄링 전략 객체 생성" 부분은 하위 클래스에서 오버라이드
(하위 클래스에서 오버라이드될 필요가 있는 메서드는 primitive 또는 hook 메서드라고 부른다.)
*** 템플릿 메서드 패턴은 전체적으로는 동일하면서 부분적으로 다른 구문으로 구성된 메서드의 코드 중복을 최소화 ***
----------> 팩토리 메서드를 호출하는 상위 클래스의 메서드 = 템플릿 메서드
'2021 > JOB DA STUDY' 카테고리의 다른 글
2주차 강의: 데이터 사이언스의 핵심 개념과 기술 (0) | 2021.04.26 |
---|---|
디자인패턴_Composite Pattern of Structural Pattern (0) | 2021.01.20 |
디자인패턴_Decorator Pattern of Structural Pattern (0) | 2021.01.19 |
디자인패턴_Observer Pattern of Behavioral Pattern (0) | 2021.01.15 |
디자인패턴_Template Pattern of Behavioral Pattern (0) | 2021.01.15 |