책너두 (헤드 퍼스트 디자인 패턴) 18일차 (~256p)
- Book/헤드 퍼스트 디자인 패턴
- 2023. 7. 18.
요약
- 호출 캡슐화하기 : 커맨드 패턴
- 커맨드 패턴의 정의
- 커맨드 패턴 클래스 다이어그램 살펴보기
- 슬롯에 명령 할당하기
- 리모컨 코드 만들기
- 커맨드 클래스 만들기
- 리모컨 테스트
- 작업 취소 기능 추가하기
- 작업 취소 기능 테스트
- 작업 취소 기능을 구현할 때 상태를 사용하는 방법
메모
커맨드 패턴의 정의
📍 커맨드 패턴(Command Pattern) : 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있음. 이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있음.
- 메타 커맨드 패턴 → 여러 개의 명령을 매크로로 한 번에 실행할 수 있음.
커맨드 패턴 클래스 다이어그램 살펴보기
- 클라이언트
- ConcreteCommand를 생성하고, Receiver를 설정함
- 인보커
- 명령이 들어있음.
- execute() 메소드를 호출해서 커맨드 객체에게 특정 작업을 수행해 달라는 요구를 함
- 커맨드 (인터페이스)
- 모든 명령은 execute() 메소드 호출로 수행됨.
- 리시버에 특정 작업을 처리하라는 지시를 전달함.
- 리시버
- 요구사항을 수행할 때 어떤 일을 처리해야 하는지 알고 있는 객체
- 구상 커맨드
- 특정 행동과 리시버를 연결해줌.
- 인보커에서 execute() 호출하면, ConcreteCommand 객체에서 리시버에 있는 메소드를 호출해서 그 작업을 처리함.
슬롯에 명령 할당하기
- 각 슬롯에 명령을 할당해서, 리모컨이 인보커가 되게 만든다.
- 사용자가 버튼을 누르면 그 버튼에 맞는 커맨드 객체의 execute() 메소드가 호출되고, 리시버(조명, 선풍기, 오디오 등)에서 특정 행동을 담당하는 메소드가 실행됨.
리모컨 코드 만들기
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed (int slot) {
offCommands[slot].execute();
}
public String toString() {
StringBuffer stringBuff = new StringBuffer();
stringBuff.append("\n-2------\n");
for (int i = 0; i < onCommands.length; i++) {
stringBuff.append("[slot " + i + "]" + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + "\n");
}
return stringBuff.toString();
}
}
- 리모컨 코드는 7개의 ON/OFF 명령을 처리할 수 있음.
- setCommand 메서드는 슬롯 번호와 그 슬롯에 저장할 ON, OFF 커맨드 객체를 인자로 받음
- 사용자가 ON, OFF 버튼을 누르면 리모컨 하드웨어에서 각 버튼에 대응되는 버튼 메소드를 호출함.
커맨드 클래스 만들기
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
}
- 조명 끌 때 쓰는 커맨드 클래스임.
public class StereoOnWithCDCommand implements Command {
Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}
- 오디오를 켤때 쓰는 커맨드 클래스임.
- 리시버에 따라 구현 로직은 조금 달라질 수 있음.
리모컨 테스트
- p246~247 참고
- RemoteControl 이 만들어 질 때, 초기 커맨드 값으로 NoCommand 값을 넣음.
- 물론 특정 슬롯을 쓰려고 할 때마다, 어떤게 로딩되어 있는지 확인할 수 있지만 귀찮아서, 아무 일도 하지 않는 커맨드 클래스를 구현해서 넣은 것임.
- NoCommand 객체는 일종의 널 객체(null object)임.
- 널 객체는 리턴할 객체도 없고, 클라이언트가 null을 처리하지 않게 하고 싶을 때 처리하면 좋음.
- 널 객체는 여러 디자인 패턴에서 유용하게 쓰임.
- 그래서 널 객체를 일종의 디자인 패턴으로 분류하기도 함.
작업 취소 기능 추가하기
public interface Command {
public void execute();
public void undo();
}
- Command 인터페이스에 undo 메소드를 추가함.
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
- execute 메소드는 불을 켜기 때문에 undo()는 불을 끄기만 하면 됨.
- 반대로 LightOffCommand 는 undo 메소드에서 light.on() 해주기만 하면 됨.
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed (int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
public void undoButtonWasPushed() {
undoCOmmand.undo();
}
public String toString() {
// toString 코드
}
}
- RemoteControl 객체는 undoCommand 를 기억하기 위한 인스턴스 변수만 관리하면 됨.
- on/off 버튼을 누를 때마다 기록함.
작업 취소 기능 테스트
- p254 참고
작업 취소 기능을 구현할 때 상태를 사용하는 방법
- ex) 선풍기 속도
- 이전 선풍기 속도의 상태를 인스턴스변수로 가지고 있어야 함. (prevSpeed)
- 이전 속도를 알고 있으면 작업 취소를 해도, 이전 속도로 돌아갈 수 있음.
- p255~256 참고
'Book > 헤드 퍼스트 디자인 패턴' 카테고리의 다른 글
책너두 (헤드 퍼스트 디자인 패턴) 20일차 (~283p) (0) | 2023.07.20 |
---|---|
책너두 (헤드 퍼스트 디자인 패턴) 19일차 (~271p) (0) | 2023.07.19 |
책너두 (헤드 퍼스트 디자인 패턴) 17일차 (~239p) (0) | 2023.07.17 |
책너두 (헤드 퍼스트 디자인 패턴) 16일차 (~226p) (0) | 2023.07.15 |
책너두 (헤드 퍼스트 디자인 패턴) 15일차 (~216p) (0) | 2023.07.13 |