해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)
문제 상황
- 상당히 많은 객체의 인터페이스
- 공통적인 인터페이스의 부재…
- 앞으로도 여러 인터페이스가 추가될 수 있음.
Command Pattern
캡슐화를 더 높은 수준으로
메소드 호출을 캡슐화
- 각각의 메소드를 인터페이스 별로 분기처리하여서 작업하는 것은 유지보수성이 떨어짐
- 작업을 요청하는 쪽(객체)과 그 작업을 처리하는 쪽(커맨드 객체)을 분리
- 커맨드 객체를 추가하여 작업
- 특정 객체에 관한 특정 작업 요청을 캡슐화
- (객체와 커맨드 객체를 분리 시)패턴이 어떻게 돌아가는지 파악하기 어려움
주문하는 과정
- 주문서는 주문 내용을 캡슐화
- 주문서는 주문 내용을 요구하는 객체
- 식사 준비에 필요한 orderUp() 메소드와 주방장 레퍼런스만 들어있음.
- 주문을 받으면 주방장 레퍼런스(작업을 처리하는 객체)에게 orderUp() 을 호출해주기만 하면 됨.
- 종업원은 주문서를 받고 orderUp() 호출
- orderUp() 메소드를 호출하는 객체
- 주문서의 내용, 주방장이 누구인지 => 아무도 관심이 없음.
- takeOrder() 메소드에는 여러 고객의 주문서를 매개변수로 전달.
- 주방장은 식사를 준비하는데 필요한 정보
- 식사 준비 방법은 주방장만 알고 있음.
용어정리
앞으로 나오는 객체의 대한 용어를 정의.
- Client
- 커맨드 객체 생성
- Command
- 리시버에 전달할 일련의 행동(execute()함수)을 구성.
- execute() 함수를 사용하여 행동을 캡슐화하고, 특정 행동을 처리
- Invoker
- 커맨드 객체를 쓰이기 전까지 보관.
- 클라이언트를 통해서 커맨드 객체를 넘겨받음.
- Receiver
- 행동 메소드 실행
커맨드 객체 만들기
- 커맨드 인터페이스 구현
- 모두 같은 인터페이스 구현
- execute() 메소드 정의
- 조명을 켤 때 필요한 커맨드 클래스 구현
- LightOnCommand 클래스 구현
- 특정 커맨드 객체로 제어한 클래스의 인터페이스 전달.(light 인터페이스)
- execute() 메소드 오버라이드
- LightOnCommand 클래스 구현
example
public class RemoteControlTest {
public static void main(String[] args) {
// Light 커맨드 객체
SimpleRemoteControl remote = new SimpleRemoteControl(); // invoker
Light light = new Light("1"); // receiver
LightOnCommand lightOn = new LightOnCommand(light); // command
remote.setCommand(lightOn); // invoker <- command
remote.buttonWasPressed();
// Garage 커맨드 객체
GarageDoor garageDoor = new GarageDoor("2");
GarageDoorOpenCommand garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor);
remote.setCommand(garageDoorOpenCommand);
remote.buttonWasPressed();
}
}
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
// Receiver
public class Light {
String location = "";
public Light(String location) {
this.location = location;
}
public void on() {
System.out.println("조명이 켜졌습니다");
}
public void off() {
}
}
// 행동을 캡슐화하기 위한 공통 interface
public interface Command {
public void execute();
public void undo();
}
// Command 객체(Light)를 실행할 Command class
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
커맨드 패턴 정의
💡 커맨드 패턴을 사용하면 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화 가능.
- 커맨드 객체는 일련의 행동을 특정 리시버와 연결. 요청을 캡슐화
- 메타 커맨드 패턴 => 여러개의 명령을 매크로로 한번에 실행 가능
Flow
- Client는 ConcreteCommand(LightOnCommand, 커맨드 객체)를 생성하고 Receiver(Light, 리시버 객체)를 설정
- Invoker(RemoteController, 인보커 객체)는 명령이 들어 있으며 execute() 호출로 커맨드 객체에게 특정 작업 수행 가능
- Command는 모든 커맨드 객체가 구현해야하는 인터페이스
- execute()로 메소드 호출
- 리시버에게 특정 작업을 처리하라는 지시 전달.
- Receiver는 요구사항을 수행할때 어떤 일을 처리해야 할 지.
- ConcreteCommand는 특정 행동과 리시버를 연결
- Invoker에서 execute() 호출로 요청하면 ConcreteCommand 객체에서 리시버의 메소드를 호출. 작업 처리.
💡 NoCommand 객체
NoCommand 객체는 일종의 null 객체.
널 객체는 딱히 리턴할 객체도 없고 클라이언트가 null을 처리하지 않게 하고 싶을 때 활용하면 좋음.
public class NoCommand implements Command {
public void execute() { }
public void undo() {}
}
'기타 > 디자인패턴' 카테고리의 다른 글
[Design Pattern] Template Method Pattern (0) | 2024.06.05 |
---|---|
[Design Pattern] Adapter Pattern (0) | 2024.06.04 |
[Design Pattern] Singleton Pattern (0) | 2024.05.31 |
[Design Pattern] Factory Pattern (0) | 2024.05.29 |
[Design Pattern] Decorator Pattern (0) | 2024.05.28 |