명령 패턴

Command pattern

객체 지향 프로그래밍에서 명령 패턴은 나중에 어떤 행동을 수행하거나 사건을 트리거하는 데 필요한 모든 정보를 캡슐화하는 데 객체를 사용하는 행동 설계 패턴이다.이 정보에는 메서드 이름, 메서드를 소유하는 개체 및 메서드 매개 변수에 대한 값이 포함된다.

명령 패턴과 항상 연관된 네 가지 용어는 명령, 수신기, 호출기클라이언트다.명령 물체는 수신기에 대해 알고 수신자의 방법을 호출한다.수신기 방법의 파라미터 값은 명령에 저장된다.이러한 방법을 실행하는 수신자 객체도 집합에 의해 명령 객체에 저장된다.다음, 수신자가 일을 한다.execute()명령하는 방법을 호출한다.호출자 개체는 명령 실행 방법을 알고 있으며, 선택적으로 명령 실행에 대한 부기를 한다.호출자는 구체적인 명령에 대해 아는 것이 없고 명령 인터페이스에 대해서만 알고 있다.인보커 객체, 명령 객체 및 수신기 객체는 클라이언트 객체에 의해 보유되며, 클라이언트는 명령 객체에 할당하는 수신기 객체와 인보커에 할당하는 명령어를 결정한다.클라이언트는 어떤 지점에서 어떤 명령을 실행할지 결정한다.명령을 실행하려면 명령 개체를 호출자 개체로 전달한다.

명령 객체를 사용하면 메서드의 클래스나 메서드 매개변수를 알 필요 없이 원하는 시간에 메서드 호출을 위임, 순서 지정 또는 실행해야 하는 일반 구성요소를 쉽게 구성할 수 있다.호출자 객체를 사용하면 클라이언트가 부기 또는 모드의 존재를 인식할 필요 없이 호출자 객체에 의해 관리되는 명령에 대해 서로 다른 모드를 구현하는 것은 물론 명령 실행에 대한 부기가 편리하게 수행될 수 있다.

이 설계 패턴의 중심 사상은 기능 프로그래밍 언어에서 1등급 함수고차 함수의 의미론을 밀접하게 반영한다.구체적으로, 호출자 객체는 명령 객체가 1종 인수인 고차 함수다.

개요

명령[1] 설계 패턴은 반복적인 설계 문제를 해결하여 유연하고 재사용 가능한 객체 지향 소프트웨어, 즉 구현, 변경, 테스트 및 재사용하기 쉬운 객체를 설계하는 방법을 기술하는 23가지 GoF 설계 패턴 중 하나이다.

명령 설계 패턴을 사용하면 다음과 같은 문제를 해결할 수 있다.[2]

  • 요청의 호출자를 특정 요청에 연결하지 않아야 한다.즉, 유선상의 요청은 피해야 한다.
  • 요청을 통해 (요청을 호출하는) 개체를 구성할 수 있어야 한다.

클래스에 직접 요청을 구현(강경화)하는 것은 컴파일 시간에 클래스를 특정 요청과 결합하기 때문에 유연성이 떨어지기 때문에 런타임에 요청을 지정할 수 없다.

명령 설계 패턴 사용에서는 다음 솔루션에 대해 설명하십시오.

  • 요청을 캡슐화하는 개별(명령) 개체를 정의하십시오.
  • 클래스는 특정 요청을 직접 구현하는 대신 명령 개체에 요청을 위임한다.

이렇게 하면 요청을 수행하는 데 사용되는 명령 개체로 클래스를 구성할 수 있다.클래스는 더 이상 특정 요청에 연결되지 않으며 요청이 수행되는 방법에 대한 지식(독립적)이 없다.

아래 UML 클래스 및 시퀀스 다이어그램을 참조하십시오.

구조

UML 클래스 및 시퀀스 다이어그램

명령 설계 패턴에 대한 샘플 UML 클래스 및 시퀀스 다이어그램.[3]

위의 UML클래스 다이어그램에서Invoker클래스는 요청을 직접 실행하지 않는다.대신,Invoker에 언급하다Command요청을 수행하기 위한 인터페이스(command.execute()() 이므로 다음과 같다.Invoker요청 수행 방식과 무관하게Command1클래스는 다음 작업을 수행한다.Command수신기에 대한 작업을 수행하여 인터페이스(Interface)receiver1.action1()).

UMLsequence 다이어그램은 런타임 교호작용을 보여준다.Invoker반대 전화execute()에서Command1이의를 제기하다 Command1전화를 하다action1()에서Receiver1요청을 수행하는 개체.

UML 클래스 다이어그램

명령 패턴의 UML 다이어그램

사용하다

GUI 버튼 및 메뉴 항목
스윙볼랜드 델파이 프로그래밍에서Action명령 객체.원하는 명령 수행 능력 외에동작에는 연결된 아이콘, 바로 가기 키, 도구 설명 텍스트 등이 있을 수 있다.도구 모음 버튼 또는 메뉴 항목 구성 요소는 Action 객체만 사용하여 완전히 초기화할 수 있다.
매크로 기록
모든 사용자 작업이 명령 개체로 표현되는 경우 프로그램은 명령 개체 목록이 실행되는 대로 유지함으로써 일련의 작업을 기록할 수 있다.그런 다음 동일한 명령 개체를 순서대로 다시 실행하여 동일한 작업을 "재생"할 수 있다.프로그램에 스크립팅 엔진이 내장되면 각 명령 개체는 toScript() 메서드를 구현할 수 있으며, 사용자 작업은 스크립트로 쉽게 기록될 수 있다.
모바일 코드
URLClassloader 및 Codebases를 통해 한 위치에서 다른 위치로 코드를 스트리밍/슬러핑할 수 있는 Java와 같은 언어를 사용하면 새 동작이 원격 위치(EJB 명령, 마스터 작업자)로 전달될 수 있다.
다단계 실행 취소
프로그램의 모든 사용자 작업이 명령 개체로 구현되는 경우 프로그램은 가장 최근에 실행된 명령의 스택을 유지할 수 있다.사용자가 명령을 실행 취소하고 싶을 때 프로그램은 최신 명령 개체를 간단히 터뜨리고 실행 취소() 방법을 실행한다.
네트워킹
컴퓨터 게임에서 플레이어의 동작과 같이 다른 기계에서 실행될 전체 명령 객체를 네트워크를 통해 전송할 수 있다.
병렬 처리
명령이 공유 리소스에 작업으로 기록되고 여러 스레드에 의해 병렬로 실행되는 경우(아마도 원격 시스템에서, 이 변형을 마스터/작업자 패턴이라고 함)
진행 표시줄
프로그램이 순서대로 실행되는 일련의 명령을 가지고 있다고 가정합시다.각 명령 객체에 getEstimatedDuration() 메서드가 있으면 프로그램은 총 기간을 쉽게 추정할 수 있다.프로그램이 모든 작업을 완료하는 데 얼마나 가까운지를 의미 있게 반영하는 진행 표시줄을 보여줄 수 있다.
스레드 풀
일반적인 범용 스레드 풀 클래스는 수행 대기 중인 작업의 내부 대기열에 작업 항목을 추가하는 공용 addTask() 메서드를 가질 수 있다.대기열에서 명령을 실행하는 스레드 풀을 유지 관리한다.대기열에 있는 항목은 명령 개체 입니다.일반적으로 이러한 개체는 java.lang과 같은 공통 인터페이스를 구현한다.스레드 풀 클래스가 사용될 특정 태스크를 전혀 알지 못한 채 작성되었음에도 스레드 풀에서 명령을 실행할 수 있도록 허용하는 실행 가능.
트랜잭션 동작
실행 취소와 유사하게 데이터베이스 엔진 또는 소프트웨어 설치자는 수행되었거나 수행될 작업 목록을 보관할 수 있다.둘 중 하나가 실패하면 다른 모든 것은 되돌리거나 폐기될 수 있다(일반적으로 롤백이라고 함).예를 들어 서로를 참조하는 두 개의 데이터베이스 테이블을 업데이트해야 하고, 두 번째 업데이트가 실패하면 트랜잭션을 롤백할 수 있으므로 첫 번째 테이블에는 이제 잘못된 참조가 포함되지 않는다.
마법사
종종 마법사는 사용자가 마지막 페이지의 "마침" 단추를 클릭할 때만 수행되는 단일 작업에 대해 여러 페이지의 구성을 제시한다.이 경우 사용자 인터페이스 코드와 애플리케이션 코드를 구분하는 자연스러운 방법은 명령 개체를 사용하여 마법사를 구현하는 것이다.명령 개체는 마법사가 처음 표시될 때 생성된다.각 마법사 페이지는 명령 객체에 GUI 변경사항을 저장하므로 사용자가 진행함에 따라 객체가 채워진다."완료"는 단순히 실행() 호출을 트리거한다.이렇게 하면 명령수업이 통할 것이다.

용어.

명령 패턴 구현을 설명하는 데 사용되는 용어는 일관성이 없으므로 혼동될 수 있다.이것은 모호성, 동의어의 사용, 그리고 그것을 훨씬 넘어서서 원래의 패턴을 모호하게 할 수 있는 실행의 결과물이다.

  1. 애매하다.
    1. 명령어가 애매하다.예를 들어 위로 이동, 위로 이동은 두 번 실행해야 하는 단일(위로 이동) 명령을 가리키거나, 두 개의 명령을 가리키기도 하며, 각각은 동일한 작업을 수행(위로 이동)할 수 있다.이전 명령이 실행 취소 스택에 두 번 추가된 경우 스택의 두 항목은 동일한 명령 인스턴스를 참조하십시오.이는 명령이 항상 같은 방식으로 실행 취소할 수 있는 경우(예: 아래로 이동) 적절할 수 있다.아래4강자바 예 모두 이 용어 명령의 해석을 사용한다.반면에, 후자의 명령이 실행 취소 스택에 추가되는 경우, 스택은 두 개의 분리된 객체를 가리킨다.이는 스택의 각 객체가 명령을 실행 취소할 수 있는 정보를 포함해야 할 때 적절할 수 있다.예를 들어 delete selection 명령을 실행 취소하려면 delete selection 명령을 실행 취소해야 하는 경우 객체에 삭제된 텍스트의 사본을 다시 삽입할 수 있도록 할 수 있다.명령의 각 호출에 대해 별도의 개체를 사용하는 것도 책임 패턴의 체인의 예라는 점에 유의하십시오.
    2. 실행이라는 용어도 애매하다.명령 개체의 실행 방법으로 식별된 코드를 실행하는 것을 가리킬 수 있다.그러나 마이크로소프트의 윈도 프리젠테이션 재단에서는 명령의 실행 방법이 호출되었을 때 명령이 실행된 것으로 간주되지만, 그것이 반드시 응용 프로그램 코드가 실행되었음을 의미하는 것은 아니다.그것은 어떤 추가적인 이벤트 처리 후에만 발생한다.
  2. 동의어와 동음이의어.
    1. 클라이언트, 소스, 인보커: 버튼, 도구 모음 버튼 또는 메뉴 항목을 클릭했으며, 사용자가 누르는 바로 가기 키.
    2. 명령 개체, 라우팅된 명령 개체, 작업 개체: 명령과 관련된 바로 가기 키, 단추 이미지, 명령 텍스트 등에 대해 알고 있는 싱글톤 개체(예: CopyCommand 개체가 하나만 있음)소스/인보커 개체는 명령/액션 개체의 실행/수행Action 메서드를 호출한다.명령/액션 개체는 명령/액션의 가용성이 변경되면 적절한 소스/인보커 개체를 통지한다.이를 통해 명령/ 동작을 실행/수행할 수 없을 때 버튼과 메뉴 항목이 비활성(회색으로 표시)될 수 있다.
    3. 수신기, 대상 객체: 복사, 붙여넣기, 이동 등을 앞둔 객체수신기 개체는 명령의 실행 방법으로 호출되는 방법을 소유한다.수신기도 일반적으로 대상 물체다.예를 들어 수신기 객체가 커서 MoveUp이라고 하는 방법을 사용하는 경우, 커서가 MoveUp 동작의 대상이 될 것으로 예상할 수 있다.반면 명령 객체 자체에 의해 코드가 정의되면 대상 객체는 완전히 다른 객체가 된다.
    4. 명령 개체, 라우팅된 이벤트 인수, 이벤트 개체: 소스에서 Command/Action 개체로 전달되고 대상 개체에서 작업을 수행하는 코드로 전달되는 개체.각각의 버튼 클릭이나 단축키는 새로운 명령/이벤트 객체를 만들어낸다.일부 구현에서는 명령/이벤트 객체가 한 객체(예: CopyCommand)에서 다른 객체(예: 문서 섹션)로 전달될 때 해당 객체에 더 많은 정보를 추가한다.다른 구현에서는 이름 충돌을 피하기 위해 더 큰 상자 안의 상자처럼 명령/이벤트 개체를 다른 이벤트 개체(예: 더 큰 상자 안의 상자)에 넣는다.(책임 패턴 체인을 참조하십시오.)
    5. Handler, ExecutiveRommotedEventHandler, method, function: 복사, 붙여넣기, 이동 등을 수행하는 실제 코드.일부 구현에서 핸들러 코드는 명령/조치 객체의 일부분이다.다른 구현에서는 코드가 수신기/대상 객체의 일부분이며, 다른 구현에서는 처리기 코드가 다른 객체와는 별도로 유지된다.
    6. Command Manager, Undo Manager, Scheduler, Queue, Dispatcher, Invoker: 명령/이벤트 개체를 실행 취소 스택 또는 재실행 스택에 놓거나, 다른 개체가 명령/이벤트 개체에 작업을 수행할 준비가 될 때까지 명령/이벤트 개체를 유지하거나, 명령/이벤트 개체를 적절한 수신기/대상 개체 또는 처리기 코드로 라우팅하는 개체.
  3. 원래 명령 패턴을 훨씬 뛰어넘는 구현.
    1. 마이크로소프트의 WPF(Windows Presentation Foundation)는 명령 패턴과 이벤트 처리를 결합한 라우팅된 명령을 도입한다.결과적으로 명령 개체는 더 이상 대상 개체에 대한 참조도 애플리케이션 코드에 대한 참조도 포함하지 않는다.대신 명령 객체의 실행 명령을 호출하면 이벤트의 터널링 또는 버블링 중에 대상과 애플리케이션 코드를 식별하는 이른바 바인딩 객체가 발생할 수 있는 이른바 실행 라우팅 이벤트가 발생하며, 이 개체는 해당 지점에서 실행된다.

"단순한" 스위치를 생각해 보십시오.이 예에서는 스위치를 두 가지 명령, 즉 불을 켜는 명령과 불을 끄도록 구성한다.

이러한 명령 패턴의 특정한 구현의 이점은 스위치를 단순한 빛이 아닌 어떤 장치와도 사용할 수 있다는 것이다.다음의 C# 구현에서 스위치는 불을 켜고 끄지만, 스위치의 생성자는 두 개의 파라미터에 대해 명령의 하위 클래스를 받아들일 수 있다.예를 들어, 엔진을 시작하도록 스위치를 구성할 수 있다.

사용. 시스템;  네임스페이스 명령 패턴 {         공중의 접점 아이코만드     {         공허하게 하다 실행();     }      /* Invoker 클래스 */     공중의 계급 스위치     {         아이코만드 _closedCommand;         아이코만드 _opencedCommand;          공중의 스위치(아이코만드 closedCommand, 아이코만드 opencommand)         {             ._closedCommand = closedCommand;             ._opencedCommand = opencommand;         }          // 회로 닫기/전원 켜기         공중의 공허하게 하다 가까운.()         {            ._closedCommand.실행();         }          // 회로 열기/전원 꺼짐         공중의 공허하게 하다 개방하다()         {             ._opencedCommand.실행();         }     }      /* 수신자가 수행할 수 있는 동작을 정의하는 인터페이스 */     공중의 접점 ISwitchable     {         공허하게 하다 파워온();         공허하게 하다 전원 끄기();     }      /* 수신기 클래스 */     공중의 계급  : ISwitchable     {         공중의 공허하게 하다 파워온()         {             콘솔.WriteLine("불이 켜져 있다");         }          공중의 공허하게 하다 전원 끄기()         {             콘솔.WriteLine("불 꺼졌다");         }     }      /* 장치 켜기 명령 - ConcreteCommand #1 */     공중의 계급 CloseSwitchCommand : 아이코만드     {         사유의 ISwitchable _전환가능;          공중의 CloseSwitchCommand(ISwitchable 바꿀 수 있는)         {             _전환가능 = 바꿀 수 있는;         }          공중의 공허하게 하다 실행()         {             _전환가능.파워온();         }     }      /* 기기 끄기 명령 - ConcreteCommand #2 */     공중의 계급 OpenSwitchCommand : 아이코만드     {         사유의 ISwitchable _전환가능;          공중의 OpenSwitchCommand(ISwitchable 바꿀 수 있는)         {             _전환가능 = 바꿀 수 있는;         }          공중의 공허하게 하다 실행()         {             _전환가능.전원 끄기();         }     }      /* 테스트 클래스 또는 클라이언트 */     내부의 계급 프로그램     {         공중의 정태의 공허하게 하다 메인(끈을 매다[] 논쟁들)         {             끈을 매다 언쟁점 = 논쟁들.길이 > 0 ? 논쟁들[0].토우퍼() : 무효의;              ISwitchable 등불 = 새로운 ();              // 각 명령에 램프 인스턴스 참조 전달             아이코만드 switchClose = 새로운 CloseSwitchCommand(등불);             아이코만드 스위치열림 = 새로운 OpenSwitchCommand(등불);              // 스위치에 명령 개체의 인스턴스 참조 전달             스위치 @스위치 = 새로운 스위치(switchClose, 스위치열림);              만일 (언쟁점 == "ON")             {                 // 스위치(Invoker)가 명령 개체에서 실행()을 호출한다.                 @스위치.가까운.();             }             다른 만일 (언쟁점 == "OFF")             {                 // 스위치(Invoker)는 명령 개체에서 실행()을 호출한다.                 @스위치.개방하다();             }             다른             {                 콘솔.WriteLine("논의 \"ON\" 또는 \"OFF\"가 필요하다.");             }         }     } } 

참고 항목

역사

인터랙티브 시스템을 구현하기 위해 명령 클래스를 사용하는 것에 대한 최초의 언급은 헨리 리버먼의 1985년 기사로 보인다.[4]실행실행 취소 방법이 있는 명령 클래스와 기록 목록을 사용하여 (복수 레벨) 실행 취소-재실행 메커니즘에 대한 첫 번째 공개 설명은 베르트랑 마이어의 책 객체 지향 소프트웨어 구축 [5]12.2절의 첫 번째(1988) 판으로 나타난다.

참조

  1. ^ Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 233ff. ISBN 0-201-63361-2.
  2. ^ "The Command design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  3. ^ "The Command design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  4. ^ Lieberman, Henry (1985). "There's more to menu systems than meets the screen". SIGGRAPH Computer Graphics: 181–189.
  5. ^ Meyer, Bertrand (1988). Object-Oriented Software Construction (1st ed.). Prentice-Hall.

외부 링크