본문으로 건너뛰기

디자인 패턴의 커맨드 패턴 (Command Pattern)

무료2015-03-06#Design_Pattern#命令模式#Command Pattern

커맨드 패턴은 요청자와 실행자의 결합을 풀기 위한 디자인 패턴입니다. 그 확장 응용에는 주로 큐 요청 (연산을 지정된 작업 스레드로 제한) 과 로그 요청 (로그 생성 및 트랜잭션 복원에 사용) 이 있습니다.

no_mkd

일.커맨드 패턴이란?

커맨드 패턴은 메서드 호출 세부 정보를 캡슐화하여 요청자와 실행자의 결합을 풉니다. 구체적인 흐름은 다음과 같습니다:

1.요청자 (클라이언트) 의 관점에서

요청자 (클라이언트) 가 요청을 보냄 -> 호출자 (시스템) 가 커맨드 객체를 생성하여 요청을 캡슐화 -> 호출자가 커맨드 객체의 지정된 메서드를 호출함 (요청이 실행됨). 분명히, 요청자는 실행자가 누구인지 전혀 알지 못하며, 구체적인 실행 세부 사항도 알지 못합니다. 물론 요청자 자체도 이를 신경 쓰지 않으며, 요청이 실행되었다는 것만 알면 됩니다.

2.실행자 (하위 구성 요소) 의 관점에서

실행자 (하위 구성 요소) 가 호출됨 -> 실행자가 내부 메서드를 호출함 (요청이 실행됨). 마찬가지로, 실행자는 요청자가 누구인지 전혀 알지 못하며, 호출자조차도 분명하지 않지만, 상관없습니다. 실행자는 자신의 본분만 잘 수행하면 되며, 리더의 상황을 알 필요는 없습니다.

3.호출자 (시스템) 의 관점에서

요청을 받음 -> 커맨드 객체를 생성하여 요청을 캡슐화 -> 적절한 시점에 커맨드 객체의 동작을 호출하여 요청을 실행함 (요청이 실행됨). 호출자는 실행자가 누구인지 알지 못하며, 요청자도 알지 못하고, 커맨드를 생성하고 커맨드가 실행되도록 제어하는 책임만 집니다. 이것으로 충분합니다.

위에서 각 객체 간의 낮은 결합 관계를 볼 수 있습니다:

요청자 (클라이언트) 와 실행자 (하위 구성 요소) 는 완전히 결합이 풀렸으며, 중개자인 호출자도 요청자와 실행자의 구체적인 세부 사항을 이해하지 못하고, 이들은 잘 보호되었습니다.这正是我们想要的。

이.예를 들다

현실 세계의 약간 복잡한 서브시스템에는 모두 일련의 커맨드가 있어야 합니다. 예를 들어 레스토랑의运行机制:

고객 A 가 레스토랑에 와서 국수 한 그릇을 주문��� (요청을 보냄) -> 카운터 직원이 기록함 (커맨드 생성) -> 직원이 주방에 주문표를 던짐 -> 요리사 C 가 빠르게 국수 한 그릇을 완성함 (요청이 실행됨). 고객은 누가 이 국수를 만드는지 알지 못하고, 카운터 직원도 알지 못하며, 요리사는 누가 이 국수를 주문했는지 알지 못하고, 국수를 만들면 쉴 수 있다는 것만 압니다. 커맨드 패턴과 매우 비슷하지 않습니까?

위의 메커니즘을 코드로 구현해 봅시다. 먼저, 커맨드 인터페이스가 필요합니다. 커맨드야말로 커맨드 패턴의 핵심이며, 커맨드가 없으면 모두 공상입니다

package CommandPattern;

/**

  • @author ayqy
  • 定义 Command 接口 */ public interface Command { public abstract void execute();//只需要定义一个统一的执行方法 }

커맨드가 있으면 실행자도 필요합니다. 그렇지 않으면 장군만 있고 병사가 없으며, 레스토랑의 실행자는 물론 요리사입니다:

package CommandPattern;

/**

  • @author ayqy

  • 定义 Chef 基类 */ public abstract class Chef { //在此定义厨师的公共属性

    /**

    • 定义烹饪方法 */ public abstract void cook(); //在此定义其它有用的方法 }

구체적인 요리사도 구현해야 합니다. 전문가에게 전문 기술:

국수를 만드는 요리사:

package CommandPattern;

/**

  • @author ayqy

  • 定义专业做面的厨师 */ public class NoodlesChef extends Chef{

    @Override public void cook() { System.out.println("做好了一碗美味的拉面"); } }

떡을 만드는 요리사:

package CommandPattern;

/**

  • @author ayqy

  • 定义专业做饼的厨师 */ public class PieChef extends Chef{

    @Override public void cook() { System.out.println("做好了一块香喷喷的大饼"); } }

준비가 되었으니, 레스토랑을 개업할 수 있습니다

삼.효과 예시

Test 클래스가 필요합니다:

package CommandPattern;

/**

  • @author ayqy

  • 实现测试类 */ public class Test {

    public static void main(String[] args) { System.out.println("Command Pattern 餐馆馆开张。。"); System.out.println("第一位客户 X 先生"); System.out.println("X 先生:你好,我需要一碗面,我饿极了"); NoodlesCommand nCmd = new NoodlesCommand(); System.out.println("柜台服务员:好的,我已经记下了,马上就好"); System.out.println("柜台服务员:厨房~~,接单"); nCmd.execute(); System.out.println("X 先生:真快啊!");

     System.out.println();
     
     System.out.println("第二位客户 XX 先生");
     System.out.println("XX 先生:你好,我需要一块饼,20 分钟后来取");
     PieCommand pCmd = new PieCommand();
     System.out.println("柜台服务员:好的,我已经记下了");
     System.out.println("15 分钟后");
     System.out.println("柜台服务员:厨房~~,接单");
     pCmd.execute();
     System.out.println("XX 先生:真准时啊!");
    

    } }

결과 예시:


예에서 볼 수 있습니다:

  1. 호출자 (카운터 직원) 는 구체적인 실행 시기를 제어할 수 있지만, 구체적인 실행자 (요리사) 의 세부 사항은 전혀 알지 못합니다
  2. 요청자 (고객) 는 레스토랑의运行机制를 전혀 알지 못하며, 주문한 요리가 요리사가 만든 것인지, 서비스 직원이 만든 것인지, 옆에서 산 것인지 알지 못합니다..
  3. 실행자 (요리사) 는 요청자의 상황을 전혀 알지 못하며, 자신의 본분만 수행할 뿐, 다른 것은 아무것도 알지 못합니다

사.커맨드 패턴의 확장

1.매크로 커맨드 (여러 커맨드를 순차적으로 실행)

"커맨드의 커맨드"를 정의하여 구현할 수 있습니다 (이러한 특수한 커맨드의 execute 메서드 내부에서는 다른 여러 커맨드의 execute 메서드를 순차적으로 호출합니다..)

2.취소

많은 고객이 와서 많은 요리를 주문했는데, 잠시 후 몇몇 고객이 기다릴 수 없어 취소를 필요로 한다면, 어떻게 구현할까요? 커맨드 목록을 유지하여 이미 생성된 커맨드를 기록합니다. 취소할 때 해당 커맨드를 찾아 취소 작업을 실행해야 합니다. 물론, 전제로 커맨드 객체가 취소를 지원해야 하며, 일부 수정이 필요합니다:

package CommandPattern;

/**

  • @author ayqy

  • 定义 Command 接口 */ public interface Command { public abstract void execute();//只需要定义一个统一的执行方法

    public abstract void undo();//定义统一的撤销方法 }

각 커맨드의 취소 작업은 다를 수 있으므로, 추상 메서드로 정의하여 서브클래스가 구체적인 작업을 구현합니다

*어떻게 여러 단계의 순차적 취소를 지원할까요? 레스토랑의 예에는 그러한 기능이 필요하지 않을 수 있지만, 다른 시나리오를 생각해 봅시다. 텍스트 편집기: 사용자가 일련의 커맨드를 발행하여 일련의 작업을 완료했지만, 나중에 그러한 작업이 필요하지 않았음을 발견하고, 사용자는 변경 사항을 취소합니다 (Ctrl + Z). 이때 내용을 복원하기 위해 반대 작업을 실행해야 합니다. 어떻게 구현할까요?

여전히 각 커맨드의 undo 동작을 구현해야 합니다 (execute 와 반대 순서의 작업을 실행하면 됩니다). 그 외에도, 초기 상태로 취소하는 것을 지원하기 위해 이미 실행된 작업을 기록하는 스택이 필요합니다

3.큐 요청

작업 스레드를 하나 생성하여 모든 연산을 담당하게 할 수 있습니다. 하나의 채널을 상상해 봅시다. 입력은 다양한 커맨드이고, 출력은 커맨드의 실행 결과입니다. 아마도 한순전 전에 작업 스레드가 떡을 만들고 있었는데, 다음 순간에는 채소를 사러 나갔을 수 있습니다..

*이렇게 하는 이점은 무엇일까요? 연산을 지정된 몇 개의 스레드로 제한하여 제어할 수 있습니다

4.로그 요청

주로 데이터베이스 관리 시스템 구현에 사용됩니다. 일련의 작업을 기록해야 합니다 (예: 하드 디스크에 쓰기). 시스템 장애를 만났을 때 읽어서 데이터를 복원합니다. 어떻게 구현할까요? 객체의 직렬화를 사용하여 객체를 저장하고 (로그 기록), 필요할 때 역직렬화합니다 (트랜잭션 복원)

오.요약

커맨드 패턴은 효과적으로 요청자와 실행자의 결합을 풀며, 추가적인 이점 (예: 취소 작업 지원, 큐 요청, 로그 기록 등) 도 제공할 수 있습니다

댓글

아직 댓글이 없습니다

댓글 작성