해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.

아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)

문제 상황


  • 상당히 많은 객체의 인터페이스
    • 공통적인 인터페이스의 부재
    • 앞으로도 여러 인터페이스가 추가될 수 있음.

Command Pattern


캡슐화를 더 높은 수준으로
메소드 호출을 캡슐화

  • 각각의 메소드를 인터페이스 별로 분기처리하여서 작업하는 것은 유지보수성이 떨어짐
  • 작업을 요청하는 쪽(객체)과 그 작업을 처리하는 쪽(커맨드 객체)을 분리
  • 커맨드 객체를 추가하여 작업
    • 특정 객체에 관한 특정 작업 요청을 캡슐화
  • (객체와 커맨드 객체를 분리 시)패턴이 어떻게 돌아가는지 파악하기 어려움

주문하는 과정

  • 주문서는 주문 내용을 캡슐화
    • 주문서는 주문 내용을 요구하는 객체
    • 식사 준비에 필요한 orderUp() 메소드와 주방장 레퍼런스만 들어있음.
    • 주문을 받으면 주방장 레퍼런스(작업을 처리하는 객체)에게 orderUp() 을 호출해주기만 하면 됨.
  • 종업원은 주문서를 받고 orderUp()  호출
    • orderUp() 메소드를 호출하는 객체
    • 주문서의 내용, 주방장이 누구인지 => 아무도 관심이 없음.
    • takeOrder() 메소드에는 여러 고객의 주문서를 매개변수로 전달.
  • 주방장은 식사를 준비하는데 필요한 정보
    • 식사 준비 방법은 주방장만 알고 있음.

용어정리

앞으로 나오는 객체의 대한 용어를 정의.

  • Client
    • 커맨드 객체 생성
  • Command
    • 리시버에 전달할 일련의 행동(execute()함수)을 구성.
    • execute() 함수를 사용하여 행동을 캡슐화하고, 특정 행동을 처리
  • Invoker
    • 커맨드 객체를 쓰이기 전까지 보관. 
    • 클라이언트를 통해서 커맨드 객체를 넘겨받음.
  • Receiver
    • 행동 메소드 실행 

커맨드 객체 만들기

  • 커맨드 인터페이스 구현
    • 모두 같은 인터페이스 구현
    • execute() 메소드 정의
  • 조명을 켤 때 필요한 커맨드 클래스 구현
    • LightOnCommand 클래스 구현
      • 특정 커맨드 객체로 제어한 클래스의 인터페이스 전달.(light 인터페이스)
    • execute() 메소드 오버라이드

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();
	}
}

커맨드 패턴 정의


💡 커맨드 패턴을 사용하면 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화 가능.
  • 커맨드 객체는 일련의 행동을 특정 리시버와 연결. 요청을 캡슐화
  • 메타 커맨드 패턴 => 여러개의 명령을 매크로로 한번에 실행 가능

Command Pattern

Flow

  • ClientConcreteCommand(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

+ Recent posts