인터프리터 패턴
Interpreter pattern이 글은 검증을 위해 인용구가 추가로 필요하다. – · · 책 · · (2008년 11월 (이 를 |
컴퓨터 프로그래밍에서, 통역 패턴은 언어로 문장을 평가하는 방법을 지정하는 디자인 패턴이다.기본적인 아이디어는 각 기호(단말기 또는 비단말기)에 대한 수업을 전문 컴퓨터 언어로 하는 것이다.언어에서 문장의 구문 트리는 복합 패턴의 한 예로서 클라이언트의 문장을 평가(해석)하는 데 사용된다.[1]: 243 복합 패턴을 참조하십시오.
개요
인터프리터 설계 패턴은 반복적인 설계 문제를 해결하여 유연하고 재사용 가능한 객체 지향 소프트웨어, 즉 구현, 변경, 테스트 및 재사용하기 쉬운 객체를 설계하는 방법을 설명하는 23가지 GoF 설계 패턴 중 하나이다.
통역사의 디자인 패턴이 해결할 수 있는 문제는 무엇인가?[3]
- 간단한 언어를 위한 문법이 정의되어야 한다.
- 언어의 문장이 해석될 수 있도록.
문제가 매우 자주 발생할 때, 통역자가 문장을 해석하여 문제를 해결할 수 있도록 간단한 언어(도메인 특정 언어)로 표현한다고 생각할 수 있다.
예를 들어, 서로 다르거나 복잡한 검색 식을 많이 지정해야 하는 경우.클래스로 직접 구현(강력 연결)하는 것은 클래스를 특정 표현식으로 커밋하고 클래스를 변경할 필요 없이 클래스와 독립적으로 새로운 표현식을 지정하거나 기존 표현식을 변경할 수 없기 때문에 융통성이 없다.
통역관 설계 패턴이 설명하는 해결책은?
- 간단한 언어의 문법을 정의하여
Expression
계층 구조 및 구현interpret()
작전 - 다음으로 구성된 추상 구문 트리(AST)에 의해 언어로 문장 표시
Expression
예시 - 전화를 걸어 문장을 해석하다.
interpret()
AST로
식 개체는 추상 구문 트리라고 하는 복합/트리 구조로 재귀적으로 구성된다(복합 패턴 참조).
해석자 패턴은 추상 구문 트리를 만드는 방법을 설명하지 않는다.이것은 클라이언트에 의해 수동으로 또는 파서에 의해 자동으로 수행될 수 있다.
아래 UML 클래스 및 개체 다이어그램을 참조하십시오.
사용하다
- SQL과 같은 전문 데이터베이스 쿼리 언어.
- 통신 프로토콜을 설명하는 데 자주 사용되는 전문 컴퓨터 언어.
- 대부분의 범용 컴퓨터 언어는 실제로 몇 개의 전문 언어를 포함하고 있다.
구조
UML 클래스 및 개체 다이어그램

위의 UML클래스 다이어그램에서Client
계급은 보통을 가리킨다.AbstractExpression
표현식 해석 인터페이스interpret(context)
.
그TerminalExpression
수업은 아이가 없고 표현을 직접 해석한다.
그NonTerminalExpression
클래스는 어린이 표현 컨테이너를 유지 관리한다(expressions
) 및 해석 요청을 다음 주소로 전달expressions
.
객체 협업 다이어그램은 런타임 상호작용을 보여준다.그Client
개체는 해석 요청을 추상 구문 트리로 보낸다.요청은 트리 구조 아래쪽으로 모든 객체로 전달된다(수행됨).
그NonTerminalExpression
사물(물건)ntExpr1,ntExpr2
) 요청을 자식 표현으로 전달한다.
그TerminalExpression
사물(물건)tExpr1,tExpr2,…
)는 해석을 직접 수행한다.
UML 클래스 다이어그램
예
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
참고 항목
참조
- ^ 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.
- ^ 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 : 복수이름 : 작성자 목록(링크) - ^ "The Interpreter design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
- ^ "The Interpreter design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
외부 링크
![]() | 위키북 컴퓨터 과학 디자인 패턴에는 다음과 같은 주제의 페이지가 있다.다양한 언어로 번역기 구현 |