인터프리터 패턴

Interpreter pattern

컴퓨터 프로그래밍에서, 통역 패턴은 언어로 문장을 평가하는 방법을 지정하는 디자인 패턴이다.기본적인 아이디어는 각 기호(단말기 또는 비단말기)에 대한 수업을 전문 컴퓨터 언어로 하는 것이다.언어에서 문장의 구문 트리복합 패턴의 한 예로서 클라이언트의 문장을 평가(해석)하는 데 사용된다.[1]: 243 복합 패턴을 참조하십시오.

개요

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

통역사의 디자인 패턴이 해결할 수 있는 문제는 무엇인가?[3]

  • 간단한 언어를 위한 문법이 정의되어야 한다.
  • 언어의 문장이 해석될 수 있도록.

문제가 매우 자주 발생할 때, 통역자가 문장을 해석하여 문제를 해결할 수 있도록 간단한 언어(도메인 특정 언어)로 표현한다고 생각할 수 있다.

예를 들어, 서로 다르거나 복잡한 검색 식을 많이 지정해야 하는 경우.클래스로 직접 구현(강력 연결)하는 것은 클래스를 특정 표현식으로 커밋하고 클래스를 변경할 필요 없이 클래스와 독립적으로 새로운 표현식을 지정하거나 기존 표현식을 변경할 수 없기 때문에 융통성이 없다.

통역관 설계 패턴이 설명하는 해결책은?

  • 간단한 언어의 문법을 정의하여Expression계층 구조 및 구현interpret()작전
  • 다음으로 구성된 추상 구문 트리(AST)에 의해 언어로 문장 표시Expression예시
  • 전화를 걸어 문장을 해석하다.interpret()AST로

식 개체는 추상 구문 트리라고 하는 복합/트리 구조로 재귀적으로 구성된다(복합 패턴 참조).
해석자 패턴은 추상 구문 트리를 만드는 방법을 설명하지 않는다.이것은 클라이언트에 의해 수동으로 또는 파서에 의해 자동으로 수행될 수 있다.

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

사용하다

  • SQL과 같은 전문 데이터베이스 쿼리 언어.
  • 통신 프로토콜을 설명하는 데 자주 사용되는 전문 컴퓨터 언어.
  • 대부분의 범용 컴퓨터 언어는 실제로 몇 개의 전문 언어를 포함하고 있다.

구조

UML 클래스 및 개체 다이어그램

인터프리터 설계 패턴에 대한 샘플 UML 클래스 및 객체 다이어그램.[4]

위의 UML클래스 다이어그램에서Client계급은 보통을 가리킨다.AbstractExpression표현식 해석 인터페이스interpret(context).
TerminalExpression수업은 아이가 없고 표현을 직접 해석한다.
NonTerminalExpression클래스는 어린이 표현 컨테이너를 유지 관리한다(expressions) 및 해석 요청을 다음 주소로 전달expressions.

객체 협업 다이어그램은 런타임 상호작용을 보여준다.Client개체는 해석 요청을 추상 구문 트리로 보낸다.요청은 트리 구조 아래쪽으로 모든 객체로 전달된다(수행됨).
NonTerminalExpression사물(물건)ntExpr1,ntExpr2) 요청을 자식 표현으로 전달한다.
TerminalExpression사물(물건)tExpr1,tExpr2,…)는 해석을 직접 수행한다.

UML 클래스 다이어그램

Interpreter UML class diagram.svg

BNF

다음의 백커스-나우르 양식 예는 통역자 패턴을 보여준다.문법

::=+++++:=식 '+' 빼기 : ::=식 '-' 변수 ::='a' 'b' 'c' ... 'z'자리 = '0' '1' ...'9' 번호 ::= 숫자 번호

다음과 같은 역폴란드 표기법 식을 포함하는 언어를 정의한다.

a b + a b + b + - b + c a - -

C#

이 구조 코드는 정의된 문법을 사용하여 구문 분석된 문장을 처리하는 통역을 제공하는 통역사 패턴을 보여준다.

사용. 시스템; 사용. 시스템.수집포괄적인;  네임스페이스 OOP;  계급 프로그램 {     정태의 공허하게 하다 메인()     {         시합을 하다 문맥 = 새로운 컨텍스트();         시합을 하다 입력하다 = 새로운 MyExpression();          시합을 하다 표현 = 새로운 OrExpression         {             왼쪽 = 새로운 EqualsExpression             {                 왼쪽 = 입력하다,                 맞다 = 새로운 MyExpression { 가치 = "4" }             },             맞다 = 새로운 EqualsExpression             {                 왼쪽 = 입력하다,                 맞다 = 새로운 MyExpression { 가치 = "4" }             }         };          입력하다.가치 = "4";         표현.해석하다(문맥);         // 출력: "참"         콘솔.WriteLine(문맥.결과.());          입력하다.가치 = "44";         표현.해석하다(문맥);         // 출력: "거짓말"         콘솔.WriteLine(문맥.결과.());     } }  계급 컨텍스트 {     공중의 쌓다<끈을 매다> 결과 = 새로운 쌓다<끈을 매다>(); }  접점 표현 {     공허하게 하다 해석하다(컨텍스트 문맥); }  추상적 계급 연산자표현 : 표현 {     공중의 표현 왼쪽 { 사유의 얻다; 세트; }     공중의 표현 맞다 { 사유의 얻다; 세트; }      공중의 공허하게 하다 해석하다(컨텍스트 문맥)     {         왼쪽.해석하다(문맥);         끈을 매다 좌값 = 문맥.결과.();          맞다.해석하다(문맥);         끈을 매다 오른쪽 값 = 문맥.결과.();          도해석(문맥, 좌값, 오른쪽 값);     }      보호의 추상적 공허하게 하다 도해석(컨텍스트 문맥, 끈을 매다 좌값, 끈을 매다 오른쪽 값); }  계급 EqualsExpression : 연산자표현 {     보호의 무효로 하다 공허하게 하다 도해석(컨텍스트 문맥, 끈을 매다 좌값, 끈을 매다 오른쪽 값)     {         문맥.결과.밀다(좌값 == 오른쪽 값 ? "진짜" : "거짓말");     } }  계급 OrExpression : 연산자표현 {     보호의 무효로 하다 공허하게 하다 도해석(컨텍스트 문맥, 끈을 매다 좌값, 끈을 매다 오른쪽 값)     {         문맥.결과.밀다(좌값 == "진짜"    오른쪽 값 == "진짜" ? "진짜" : "거짓말");     } }  계급 MyExpression : 표현 {     공중의 끈을 매다 가치 { 사유의 얻다; 세트; }      공중의 공허하게 하다 해석하다(컨텍스트 문맥)     {         문맥.결과.밀다(가치);     } } 

자바

통역 패턴에 따라 문법 규칙별로 람다(수업이 될 수 있음)로 엑스퍼 인터페이스를 구현해야 한다.

공중의 계급 통역사 {     @기능적인터페이스     공중의 접점 엑스퍼 {         인트로 해석하다(지도<, 정수> 문맥);                  정태의 엑스퍼 번호를 붙이다(인트로 번호를 붙이다) {             돌아오다 문맥 -> 번호를 붙이다;         }                  정태의 엑스퍼 더하기(엑스퍼 남겨진, 엑스퍼 맞다) {             돌아오다 문맥 -> 남겨진.해석하다(문맥) + 맞다.해석하다(문맥);         }                  정태의 엑스퍼 빼다(엑스퍼 남겨진, 엑스퍼 맞다) {             돌아오다 문맥 -> 남겨진.해석하다(문맥) - 맞다.해석하다(문맥);         }                  정태의 엑스퍼 가변적( 이름을 붙이다) {             돌아오다 문맥 -> 문맥.getOrDefault(이름을 붙이다, 0);         }     } 

통역 패턴이 파싱은 다루지 않지만,[1]: 247 완전성을 위해 파서가 제공된다.

    사유의 정태의 엑스퍼 파르세토켄( 토큰, 배열Deque<엑스퍼> 쌓다) {         엑스퍼 남겨진, 맞다;         바꾸다(토큰) {         케이스 "+":             // 먼저 스택에서 오른쪽 피연산자를 제거해야 함             맞다 = 쌓다.펑펑 터지다();             // ...그리고 왼쪽.             남겨진 = 쌓다.펑펑 터지다();             돌아오다 엑스퍼.더하기(남겨진, 맞다);         케이스 "-":             맞다 = 쌓다.펑펑 터지다();             남겨진 = 쌓다.펑펑 터지다();             돌아오다 엑스퍼.빼다(남겨진, 맞다);         체납:             돌아오다 엑스퍼.가변적(토큰);         }     }     공중의 정태의 엑스퍼 파스를 치다( 표현) {         배열Deque<엑스퍼> 쌓다 = 새로운 배열Deque<엑스퍼>();         을 위해 ( 토큰 : 표현.갈라지다(" ")) {             쌓다.밀다(파르세토켄(토큰, 쌓다));         }         돌아오다 쌓다.펑펑 터지다();     } 

마지막으로 w = 5, x = 10, z = 42를 사용하여 "w x z - +" 식을 평가한다.

    공중의 정태의 공허하게 하다 본래의(최종의 [] 아그) {         엑스퍼 생략하다 = 파스를 치다("w x z - +");         지도<, 정수> 문맥 = 지도.("w", 5, "x", 10, "z", 42);         인트로 결과 = 생략하다.해석하다(문맥);         시스템.밖으로.인쇄하다(결과);        // -27     } } 

PHP(예 1)

/** * 추상적 표현 */ 접점 표현 {     공중의 기능을 발휘하다 해석하다(배열하다 달러화): 인트로; } 
/** * TerminalExpression(터미널 표현) */ 계급 터미널표현 기구들 표현 {     /*** @var 문자열 */     사유의 달러명;      공중의 기능을 발휘하다 ____(끈을 매다 달러명)     {         $ this->이름을 붙이다 = 달러명;     }      공중의 기능을 발휘하다 해석하다(배열하다 달러화): 인트로     {         돌아오다 내면의(달러화[$ this->이름을 붙이다]);     } } 
/** * NonTerminalExpression */ 추상적 계급 NonTerminalExpression 기구들 표현 {     /*** @var 표현식 $left */     보호의 달러좌차액;      /*** @var?표현식 $right */     보호의 권리금;      공중의 기능을 발휘하다 ____(표현 달러좌차액, ?표현 권리금)     {         $ this->남겨진 = 달러좌차액;         $ this->맞다 = 권리금;     }      추상적 공중의 기능을 발휘하다 해석하다(배열하다 달러화): 인트로;          공중의 기능을 발휘하다 겟라이트()     {         돌아오다 $ this->맞다;     }      공중의 기능을 발휘하다 셋라이트(권리금): 공허하게 하다     {         $ this->맞다 = 권리금;     } } 
/** * NonTerminalExpression - PlusExpression */ 계급 PlusExpression 연장하다 NonTerminalExpression {     공중의 기능을 발휘하다 해석하다(배열하다 달러화): 인트로     {         돌아오다 내면의($ this->남겨진->해석하다(달러화) + $ this->맞다->해석하다(달러화));     } } 
/** * NonTerminalExpression - MinusExpression */ 계급 빼기표현 연장하다 NonTerminalExpression {     공중의 기능을 발휘하다 해석하다(배열하다 달러화): 인트로     {         돌아오다 내면의($ this->남겨진->해석하다(달러화) - $ this->맞다->해석하다(달러화));     } } 
/** *고객 */ 계급 인터프리터클라이언트 {     보호의 기능을 발휘하다 구문 분석 목록(배열하다 &$stack, 배열하다 달러리스트, 인트로 &달러지수)     {         /*** @var 문자열 $property */         달러화 = 달러리스트[달러지수];          바꾸다(달러화) {             케이스 '-':                 리스트를 작성하다(달러좌차액, 권리금) = $ this->fetechArguments($stack, 달러리스트, 달러지수);                 돌아오다 새로운 빼기표현(달러좌차액, 권리금);             케이스 '+':                 리스트를 작성하다(달러좌차액, 권리금) = $ this->fetechArguments($stack, 달러리스트, 달러지수);                 돌아오다 새로운 PlusExpression(달러좌차액, 권리금);             체납:                 돌아오다 새로운 터미널표현(달러화);         }     }      보호의 기능을 발휘하다 fetechArguments(배열하다 &$stack, 배열하다 달러리스트, 인트로 &달러지수): 배열하다     {         /*** @var 표현식 $left */         달러좌차액 = array_pop($stack);         /*** @var 표현식 $right */         권리금 = array_pop($stack);         만일 (권리금 === 무효의) {             ++달러지수;             $ this->parseListAndPush($stack, 달러리스트, 달러지수);             권리금 = array_pop($stack);         }          돌아오다 배열하다(달러좌차액, 권리금);     }      보호의 기능을 발휘하다 parseListAndPush(배열하다 &$stack, 배열하다 달러리스트, 인트로 &달러지수)     {         array_properties($stack, $ this->구문 분석 목록($stack, 달러리스트, 달러지수));     }      보호의 기능을 발휘하다 파스를 치다(끈을 매다 $data): 표현     {         $stack = [];         달러리스트 = 폭발시키다(' ', $data);         을 위해 (달러지수=0; 달러지수<수를 세다(달러리스트); 달러지수++) {             $ this->parseListAndPush($stack, 달러리스트, 달러지수);         }          돌아오다 array_pop($stack);     }      공중의 기능을 발휘하다 본래의()     {         $data = "u + v - w + z";         $exr = $ this->파스를 치다($data);         달러화 = ['u' => 3, 'v' => 7, 'w' => 35, 'z' => 9];         $res = $exr->해석하다(달러화);         메아리치다 "result:$res" . PHP_EOL;     } } 
// 테스트.php  기능을 발휘하다 loadClass($className) {     필요_한 번 __DIR__ . "/$className.php"; }  spl_autoload_register('로드 클래스');  (새로운 인터프리터클라이언트())->본래의(); //필수: -16 

PHP(예 2)

위의 예에 근거하여 클라이언트의 또 다른 실현을 실시한다.

/** *고객 */ 계급 인터프리터클라이언트 {     공중의 기능을 발휘하다 파르세토켄(끈을 매다 달러화, 배열하다 &$stack): 표현     {         바꾸다(달러화) {             케이스 '-':                 /*** @var 표현식 $left */                 달러좌차액 = array_pop($stack);                 /*** @var 표현식 $right */                 권리금 = array_pop($stack);                 돌아오다 새로운 빼기표현(달러좌차액, 권리금);             케이스 '+':                 /*** @var 표현식 $left */                 달러좌차액 = array_pop($stack);                 /*** @var 표현식 $right */                 권리금 = array_pop($stack);                 돌아오다 새로운 PlusExpression(달러좌차액, 권리금);             체납:                 돌아오다 새로운 터미널표현(달러화);         }     }      공중의 기능을 발휘하다 파스를 치다(끈을 매다 $data): 표현     {         $미완료 데이터 = 무효의;         $stack = [];         달러리스트 = 폭발시키다(' ', $data);         앞을 내다 (달러리스트 로서 달러화) {             $data = $ this->파르세토켄(달러화, $stack);             만일 (                 ($미완료 데이터 의 예. NonTerminalExpression) &&                 ($data 의 예. 터미널표현)             ) {                 $미완료 데이터->셋라이트($data);                 array_properties($stack, $미완료 데이터);                 $미완료 데이터 = 무효의;                 계속하다;             }             만일 ($data 의 예. NonTerminalExpression) {                 만일 ($data->겟라이트() === 무효의) {                     $미완료 데이터 = $data;                     계속하다;                 }             }             array_properties($stack, $data);         }          돌아오다 array_pop($stack);     }      공중의 기능을 발휘하다 본래의()     {         $data = "u + v - w + z";         $exr = $ this->파스를 치다($data);         달러화 = ['u' => 3, 'v' => 7, 'w' => 35, 'z' => 9];         $res = $exr->해석하다(달러화);         메아리치다 "result:$res" . PHP_EOL;     } } 

자바스크립트

자바스크립트는 동적으로 입력되기 때문에 인터페이스를 구현하지 않는다.

// 비단어적 표현식 계급 더하기 {     a;     b;     건설업자(a, b) {         .a = a;         .b = b;     }     해석하다(문맥) {         돌아오다 .a.해석하다(문맥) + .b.해석하다(문맥);     } } // 비단어적 표현식 계급 빼기 {     a;     b;     건설업자(a, b) {         .a = a;         .b = b;     }     해석하다(문맥) {         돌아오다 .a.해석하다(문맥) - .b.해석하다(문맥);     } } // 비단어적 표현식 계급 시대 {     a;     b;     건설업자(a, b) {         .a = a;         .b = b;     }     해석하다(문맥) {         돌아오다 .a.해석하다(문맥) * .b.해석하다(문맥);     } } // 비단어적 표현식 계급 나누다 {     a;     b;     건설업자(a, b) {         .a = a;         .b = b;     }     해석하다(문맥) {         돌아오다 .a.해석하다(문맥) / .b.해석하다(문맥);     } } // 터미널 표현식 계급 숫자 {     a;     건설업자(a, b) {         .a = a;     }     해석하다(문맥) {         돌아오다 .a;      } } // 터미널 표현식 계급 변수 {     a;     건설업자(a) {         .a = a;     }     해석하다(문맥) {         돌아오다 문맥[.a]    0;     } } // 클라이언트 계급 파스 {     문맥;     건설업자(문맥) {         .문맥 = 문맥;     }     파스를 치다(표현) {         하게 하다 토큰 = 표현.갈라지다(" ");         하게 하다 줄을 서다 = [];         을 위해 (하게 하다 토큰  토큰) {             바꾸다 (토큰) {                 케이스 "+":                     시합을 하다 b = 줄을 서다.펑펑 터지다();                     시합을 하다 a = 줄을 서다.펑펑 터지다();                     시합을 하다 생략하다 = 새로운 더하기(a, b);                     줄을 서다.밀다(생략하다);                 부숴뜨리다;                 케이스 "/":                     시합을 하다 b = 줄을 서다.펑펑 터지다();                     시합을 하다 a = 줄을 서다.펑펑 터지다();                     시합을 하다 생략하다 = 새로운 나누다(a, b);                     줄을 서다.밀다(생략하다);                 부숴뜨리다;                 케이스 "*":                     시합을 하다 b = 줄을 서다.펑펑 터지다();                     시합을 하다 a = 줄을 서다.펑펑 터지다();                     시합을 하다 생략하다 = 새로운 시대(a, b);                     줄을 서다.밀다(생략하다);                 부숴뜨리다;                 케이스 "-":                     시합을 하다 b = 줄을 서다.펑펑 터지다();                     시합을 하다 a = 줄을 서다.펑펑 터지다();                     시합을 하다 생략하다 = 새로운 빼기(a, b);                     줄을 서다.밀다(생략하다);                 부숴뜨리다;                 체납:                     만일(isNaN(토큰)) {                         시합을 하다 생략하다 = 새로운 변수(토큰);                         줄을 서다.밀다(생략하다);                        } 다른 {                         시합을 하다 번호를 붙이다 = 파스인트(토큰);                         시합을 하다 생략하다 = 새로운 숫자(번호를 붙이다);                         줄을 서다.밀다(생략하다);                     }                 부숴뜨리다;             }          }         하게 하다 본래의 = 줄을 서다.펑펑 터지다();         돌아오다 본래의.해석하다(.문맥);     } } 시합을 하다 재방송하다 = 새로운 파스({v: 45}).파스를 치다("16 v * 76 22 -"); 위로하다.통나무를 하다(재방송하다) //666 

참고 항목

참조

  1. ^ a b Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2.
  2. ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 243ff. ISBN 0-201-63361-2.{{cite book}}: CS1 maint : 복수이름 : 작성자 목록(링크)
  3. ^ "The Interpreter design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  4. ^ "The Interpreter design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.

외부 링크