방문자 패턴

Visitor pattern

객체 지향 프로그래밍소프트웨어 엔지니어링에서, 방문자 설계 패턴은 알고리즘을 그것이 작동하는 객체 구조로부터 분리하는 방법이다.이러한 분리의 실질적인 결과는 구조를 수정하지 않고 기존 객체 구조에 새로운 연산을 추가할 수 있는 능력이다.그것은 개방/폐쇄 원칙을 따르는 한 가지 방법이다.

본질적으로 방문자는 클래스를 수정하지 않고 클래스의 가족에 새로운 가상 기능을 추가하는 것을 허용한다.대신 가상 기능의 모든 적절한 전문화를 구현하는 방문자 클래스가 생성된다.방문자는 인스턴스(instance) 참조를 입력으로 받아들이고, 더블 디스패치를 통해 목표를 구현한다.

개요

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

방문자 디자인 패턴이 해결할 수 있는 문제는 무엇인가?

  • 클래스를 변경하지 않고 객체 구조의 (일부) 클래스에 대한 새 작업을 정의할 수 있어야 한다.

새로운 운영이 자주 필요하고 개체 구조가 관련 없는 여러 클래스로 구성될 때, "[..] 이러한 모든 운영을 다양한 노드 클래스에 분산시키면 이해, 유지, 변경하기 어려운 시스템으로 이어지기 때문에 새로운 운영이 필요할 때마다 새로운 하위 클래스를 추가하는 것이 융통성이 없다.[1]

방문자 설계 패턴에서 설명하는 해결책은?

  • 객체 구조의 요소에 대해 수행할 작업을 구현하는 별도의 (방문자) 객체를 정의한다.
  • 클라이언트는 객체 구조를 트래버스하고 요소에 대한 발송 작업 수락(방문자)을 호출한다. 즉, "수락된 방문자 객체"에 대한 요청을 "배송"(위임)한다.그런 다음 방문자 객체는 요소에 대한 연산을 수행한다("요소 방문").

이를 통해 새로운 방문자 객체를 추가함으로써 객체 구조의 클래스와는 독립적으로 새로운 운영을 창출할 수 있다.

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

정의

4대강은 방문자를 다음과 같이 정의한다.

객체 구조의 요소에 대해 수행할 작업을 나타낸다.방문자는 당신이 그것이 작동하는 요소의 클래스를 변경하지 않고 새로운 연산을 정의할 수 있도록 한다.

방문자의 특성은 공개 API에 연결하기 위한 이상적인 패턴으로, 따라서 방문자의 클라이언트가 소스를 수정하지 않고도 "방문" 클래스를 사용하여 클래스에 대한 작업을 수행할 수 있게 한다.[2]

이점

방문자 클래스로의 이동은 다음과 같은 경우에 유익하다.

  • 객체 구조에서 관련 없는 많은 작업이 필요하다.
  • 객체 구조를 구성하는 클래스는 알려져 있고 변경될 것으로 예상되지 않는다.
  • 새로운 운영은 자주 추가되어야 한다.
  • 알고리즘은 객체 구조의 여러 클래스를 포함하지만, 하나의 단일 위치에서 관리하기를 원한다.
  • 알고리즘은 몇 개의 독립적인 클래스 계층 구조에서 작동할 필요가 있다.

그러나 이러한 패턴의 단점은 새로운 클래스가 일반적으로 새로운 클래스를 요구하기 때문에 클래스 계층으로의 확장을 더욱 어렵게 한다는 것이다.visit각 방문자에 추가할 방법

적용

2D CAD(Computer-Aided Design) 시스템의 설계를 고려하십시오.그 중심에는 원, 선, 호와 같은 기본적인 기하학적 형태를 나타내는 여러 종류가 있다.도면요소는 도면층으로 정렬되며, 도면요소는 도면요소에 도면요소를 더한 도면요소에 도면요소를 추가했다.

이러한 유형의 계층 구조에 대한 기본 작업은 도면을 시스템의 기본 파일 형식으로 저장하는 것이다.얼핏 보기에는 계층의 모든 유형에 로컬 저장 방법을 추가하는 것이 허용될 수 있다.그러나 도면을 다른 파일 형식으로 저장할 수 있는 것도 유용하다.훨씬 더 많은 다른 파일 포맷으로 저장하는 방법을 추가하면 비교적 순수한 원래의 기하학적 데이터 구조를 곧 혼란스럽게 한다.

이를 해결하기 위한 순진한 방법은 각 파일 형식에 대해 별도의 기능을 유지하는 것이다.이러한 저장 기능은 도면을 입력으로 사용하고, 도면을 트래버스하여 특정 파일 형식으로 인코딩할 수 있다.이것은 추가된 각각의 다른 형식에 대해 수행되므로, 기능들 간의 중복이 누적된다.예를 들어 원 모양을 래스터 형식으로 저장하려면 어떤 특정한 래스터 형태를 사용하든 매우 유사한 코드가 필요하며, 다른 원시 형태와는 다르다.선이나 다각형과 같은 다른 원시적인 형태에 대한 사례는 비슷하다.따라서 코드는 객체를 가로지르는 큰 외부 루프가 되고, 루프 안에 있는 큰 결정 트리가 객체의 유형을 쿼리한다.이 접근방식의 또 다른 문제는 하나 이상의 세이버에서 형상을 놓치거나 새로운 원시 형상이 도입되는 것이 매우 쉽지만 저장 루틴은 다른 파일 형식이 아닌 하나의 파일 형식에 대해서만 실행되어 코드 확장 및 유지관리 문제가 발생한다는 점이다.같은 파일의 버전이 커질수록 그것을 유지하는 것은 더 복잡해진다.

대신 방문자 패턴을 적용할 수 있다.그것은 전체 계층에 대한 논리적 연산을 유형당 하나의 메서드를 포함하는 하나의 클래스로 인코딩한다.CAD 예제에서 각 저장 기능은 별도의 방문자 하위 등급으로 구현될 것이다.이렇게 하면 유형 점검 및 통과 단계의 모든 중복을 제거할 수 있다.그것은 또한 모양을 생략하면 컴파일러가 불평하게 만들 것이다.

반복 루프

방문자 패턴은 Iterator 패턴과 마찬가지로 컨테이너와 같은 데이터 구조 위에 반복하는데 사용될 수 있지만 기능이 제한적이다.[3]: 288 예를 들어 디렉토리 구조에 대한 반복은 더 전통적인 루프 패턴 대신 함수 클래스에 의해 구현될 수 있다.이것은 반복 코드를 재사용하는 동안 모든 항목에 대해 방문자 기능을 구현함으로써 디렉토리 내용으로부터 다양한 유용한 정보를 도출할 수 있다.스몰토크 시스템에도 널리 채용되어 있으며, C++에서도 찾아볼 수 있다.[3]: 289 그러나 이 접근 방식의 단점은 루프에서 쉽게 이탈하거나 동시에 반복할 수 없다는 것이다(예: 두 컨테이너를 한 번에 동시에 통과).i가변적[3]: 289 후자는 방문자가 이러한 기능을 지원하기 위해 추가적인 기능성을 작성할 것을 요구할 것이다.[3]: 289

구조

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

방문자 설계 패턴에 대한 샘플 UML 클래스 다이어그램 및 시퀀스 다이어그램.[4]

위의 UML클래스 다이어그램에서ElementA클래스는 새로운 작업을 직접 수행하지 않는다.대신,ElementA파견 작전을 실시하다. accept(visitor)"방문객 대상"에 대한 요청 "시계" (표시)visitor.visitElementA(this). The.Visitor1class에서 작업 구현(visitElementA(e:ElementA)).
ElementB그리고 나서 도구들accept(visitor)에 파견하여.visitor.visitElementB(this). TheVisitor1class에서 작업 구현(visitElementB(e:ElementB)).

UMLsequence 다이어그램은 런타임 교호작용을 보여준다.Client객체는 객체 구조의 요소를 통과한다(ElementA,ElementB) 및 호출accept(visitor)각 방면에
첫째, 더Client전화를 하다accept(visitor)에 관하여ElementA, 그래서 전화한다.visitElementA(this)합격하여visitor이의를 제기하다요소 자체().this)는 에 전달된다.visitor그것이 "눈"할 수 있도록ElementA(통화)operationA()).
그 후에Client전화를 하다accept(visitor)에 관하여ElementB, 그래서 전화한다.visitElementB(this)에서visitor저 "visits"ElementB(calls)operationB()).

클래스 다이어그램

UML(Unified Modeling Language) 방문자.[5]: 381
LePUS3 방문자(레전드)

세부 사항

방문자 패턴은 공통 객체 지향 언어(C++, 자바, 스몰토크, 목표-C, 스위프트, 자바스크립트, 파이톤, C# 등)가 그러하듯이 단일 배달을 지원하는 프로그래밍 언어가 필요하다.이 조건에서는 두 개의 물체를 고려한다. 각 물체는 각 등급 유형이다. 하나는 원소라고 부르고 다른 하나는 방문자라 한다.

방문자가 선언하다.visit각 요소 클래스에 대해 요소를 인수로 사용하는 방법.방문자 클래스에서 구체적인 방문자를 도출하여 이를 구현한다.visit각각 개체 구조에서 작동하는 알고리즘의 일부를 구현하는 방법.알고리즘의 상태는 콘크리트 방문자 등급에 의해 국소적으로 유지된다.

원소가 선언하다.accept방문자를 논쟁으로 삼아, 방문자를 받아들이는 방법.요소 등급에서 파생된 콘크리트 요소들은 다음을 구현한다.accept방법,가장 간단한 형태로, 이것은 방문자의 방문에 지나지 않는다.visit방법,하위 개체 목록을 유지하는 복합 요소는 일반적으로 이러한 항목 위에 반복되며 각 하위 개체의 목록을 부른다.accept방법,

고객이 직접 또는 간접적으로 객체 구조를 만들어 콘크리트 방문자를 인스턴스화한다.방문자 패턴을 사용하여 수행하는 작업을 수행할 때, 다음 작업을 호출한다.accept최상위 요소의 방법.

언제accept프로그램에서 메소드를 호출하고, 그 구현은 원소의 동적 유형과 방문자의 정적 유형에 기초하여 선택된다.연결된 경우visit방법이라고 하며, 그것의 구현은 방문자의 동적 타입과 요소의 정적 타입에 기초하여 선택된다. (이행의 실행 내에서 알려진 바와 같이)accept원소의 동적 유형과 같은 방법. (보너스로서 방문자가 주어진 원소 유형의 인수를 처리할 수 없다면 컴파일러가 오류를 잡을 것이다.)

따라서, 의 이행은 다음과 같다.visit방법은 원소의 동적 유형과 방문자의 동적 유형 둘 다에 기초하여 선택된다.이것은 효과적으로 이중 파견을 구현한다.DLR(Dynamic Language Runtime)을 통한 C# 또는 C#와 같이 객체 시스템이 다중 발송을 지원하는 언어의 경우, 방문자 패턴의 구현이 크게 단순화된다(예.k.a).동적 방문자) 방문 중인 모든 사례를 커버하기 위해 간단한 기능 과부하를 사용할 수 있도록 허용.동적 방문자는 공개 데이터로만 운영된다면 개방/폐쇄 원칙(외부 구조를 수정하지 않기 때문에)과 단일 책임 원칙(별도 구성요소에서 방문자 패턴을 구현하기 때문에)을 준수한다.

이렇게 해서 하나의 알고리즘을 작성해 요소의 그래프를 가로지를 수 있으며, 그 가로지르는 동안 요소와 방문자 모두의 동적 유형을 바탕으로 요소와 상호작용할 수 있는 다른 종류의 방문자를 공급함으로써 많은 다양한 종류의 연산을 수행할 수 있다.

C# 예제

이 예는 별개의 것을 선언한다.ExpressionPrintingVisitor인쇄를 담당하는 수업

네임스페이스 위키백과;  공중의 계급 표현식PrintingVisitor {     공중의 공허하게 하다 프린트리터럴(리터럴 문자 그대로의)     {         콘솔.WriteLine(문자 그대로의.가치);     }          공중의 공허하게 하다 Print(덧셈 덧셈)     {         곱절로 하다 좌값 = 덧셈.왼쪽.GetValue();         곱절로 하다 오른쪽 값 = 덧셈.맞다.GetValue();         시합을 하다 합계를 내다 = 덧셈.GetValue();         콘솔.WriteLine("{0} + {1} = {2}", 좌값, 오른쪽 값, 합계를 내다);     } }  공중의 추상적 계급 표현 {         공중의 추상적 공허하게 하다 받아들이다(표현식PrintingVisitor v);          공중의 추상적 곱절로 하다 GetValue(); }  공중의 계급 리터럴 : 표현 {     공중의 곱절로 하다 가치 { 얻다; 세트; }      공중의 리터럴(곱절로 하다 가치를 매기다)     {         .가치 = 가치를 매기다;     }          공중의 무효로 하다 공허하게 하다 받아들이다(표현식PrintingVisitor v)     {         v.프린트리터럴();     }          공중의 무효로 하다 곱절로 하다 GetValue()     {         돌아오다 가치;     } }  공중의 계급 덧셈 : 표현 {     공중의 표현 왼쪽 { 얻다; 세트; }     공중의 표현 맞다 { 얻다; 세트; }      공중의 덧셈(표현 남겨진, 표현 맞다)     {         왼쪽 = 남겨진;         맞다 = 맞다;     }          공중의 무효로 하다 공허하게 하다 받아들이다(표현식PrintingVisitor v)     {         왼쪽.받아들이다(v);         맞다.받아들이다(v);         v.Print();     }          공중의 무효로 하다 곱절로 하다 GetValue()     {         돌아오다 왼쪽.GetValue() + 맞다.GetValue();         } }  공중의 정태의 계급 프로그램 {     공중의 정태의 공허하게 하다 메인(끈을 매다[] 아그)     {         // 1 + 2 + 3 에뮬레이트         시합을 하다 e = 새로운 덧셈(             새로운 덧셈(                 새로운 리터럴(1),                 새로운 리터럴(2)             ),             새로운 리터럴(3)         );                  시합을 하다 인쇄 방문자 = 새로운 표현식PrintingVisitor();         e.받아들이다(인쇄 방문자);     } } 

스몰토크 예제

이 경우, 개울에 인쇄하는 방법을 아는 것은 객체의 책임이다.그렇다면 이곳의 방문객은 개울이 아니라 물체다.

"클래스를 만드는 데는 구문이 없어. 클래스는 다른 클래스에 메시지를 보내면 만들어진다."WriteStream 하위 클래스: #ExpressionPrinter instanceVariableNames:' classVariableNames:' 패키지: 'Wikipedia'ExpressionPrinter >>write: anObject "사물에 대한 액션을 위임한다. 개체특별한 등급이 될 필요가 없으며 #putOn:" anObject putOn: self라는 메시지를 이해할 수 있어야만 한다.^ anObject.개체 하위 클래스: #Expression instanceVariableNames:' classVariableNames:' 패키지: 'Wikipedia'표현식 하위 클래스: #Literal 인스턴스VariableNames: 'value' classVariableNames:' 패키지: 'Wikipedia'리터럴 클래스 >><with: aValue "리터럴 클래스의 인스턴스(instance of the Literal class)" ^ 자기 새로운 값: aValue; 당신 자신.리터럴 >>값 : aValue "Setter for value":= aValue.리터럴 >>putOn: aStream "A Literal object know to printing" aStream nextPutAll: value asString.표현식 하위 클래스: #AdditionVariableNames: '좌우' classVariableNames:' 패키지: 'Wikipedia'추가수업>>왼쪽 : b: b "추가수업의 인스턴스(instance) 구축하기 위한 클래스방법" ^ 자기 새로운 좌: a; 오른쪽:b; 당신 자신.덧셈>>왼쪽 : anExpression "Setter for Left" 왼쪽 := anExpression.추가>>>오른쪽 : 표현 "오른쪽 대한 설정" 오른쪽 :=표현추가>>putOn: aStream "Addition object know who putself" aStream nextPut: $(. 왼쪽 putOn: aStream. aStream nextPut: $+. 오른쪽 putOn: aStream. aStream nextPut: $)개체 하위 클래스: #Program instanceVariableNames:' classVariableNames:' 패키지: 'Wikipedia'프로그램>>>메인 표현 스트림 표현 :=왼쪽 추가 : (왼쪽 추가 : 1) 오른쪽 : (왼쪽 : 1) 오른쪽 : (2) 오른쪽 : (문자 : 3) 스트림 :=표현프린터 켜기 : ( 문자열 : 100)스트림 쓰기: 표현대본 쇼: 스트림 컨텐츠.대본 플러시. 


가다

바둑은 과부하를 지원하지 않기 때문에 방문 방법은 다른 이름이 필요하다.일반적인 방문자 인터페이스는

타자를 치다 방문자 접점 {  방문휠(바퀴를 돌리다 바퀴) 끈을 매다  방문엔진(엔진 엔진) 끈을 매다  방문바디(보디 ) 끈을 매다  방문차(자동차 자동차) 끈을 매다 } 

자바 예제

다음 예는 자바어(Java)로, 노드 트리(이 경우 자동차의 구성요소를 기술)의 내용을 인쇄할 수 있는 방법을 보여준다.만드는 대신print각 노드 하위 클래스에 대한 방법(Wheel,Engine,Body그리고Car), 방문자 클래스 1개().CarElementPrintVisitor)는 필요한 인쇄 작업을 수행한다.서로 다른 노드 하위 클래스가 올바르게 인쇄하려면 약간 다른 작업이 필요하기 때문에,CarElementPrintVisitor그 논거에 전달된 계급을 근거로 하여 행동을 파견하다.visit방법, CarElementDoVisitor다른 파일 형식에 대한 저장 작업과 유사하게 , 또한 그렇게 한다.

도표

UML diagram of the Visitor pattern example with Car Elements

원천

수입하다 java.util.리스트;  접점 캐리어먼트 {     공허하게 하다 받아들이다(카레멘털바이저 방문자); }  접점 카레멘털바이저 {     공허하게 하다 방문하다( 보디);     공허하게 하다 방문하다(자동차 자동차);     공허하게 하다 방문하다(엔진 엔진);     공허하게 하다 방문하다(바퀴 바퀴를 돌리다); }  계급 바퀴 기구들 캐리어먼트 {   사유의 최종의  이름을 붙이다;    공중의 바퀴(최종의  이름을 붙이다) {       .이름을 붙이다 = 이름을 붙이다;   }    공중의  getName() {       돌아오다 이름을 붙이다;   }    @오버라이드   공중의 공허하게 하다 받아들이다(카레멘털바이저 방문자) {       /* * 휠 장비에서 수락(CarElementVisitor) * CarElement에서 (CarElementVisitor)를 수락하십시오. 따라서 호출 * 수락은 런타임에 구속된다.이것은 고려될 수 있다. * *첫 번째* 파견.하지만, 전화하기로 한 결정은 * 방문(휠) (방문(엔진) 등과는 반대) 가능 * 컴파일할 때 '이것'이 알려지기 때문에 컴파일 시간 동안 작성됨 * 바퀴가 될 시간.더욱이, 각각의 이행은 * CarElementVisitor에서 방문(휠) 구현 * 런타임에 이루어지는 또 다른 결정.이것은 될 수 있다. * *초* 파견 고려. */       방문자.방문하다();   } }  계급  기구들 캐리어먼트 {   @오버라이드   공중의 공허하게 하다 받아들이다(카레멘털바이저 방문자) {       방문자.방문하다();   } }  계급 엔진 기구들 캐리어먼트 {   @오버라이드   공중의 공허하게 하다 받아들이다(카레멘털바이저 방문자) {       방문자.방문하다();   } }  계급 자동차 기구들 캐리어먼트 {     사유의 최종의 리스트<캐리어먼트> 요소들;      공중의 자동차() {         .요소들 = 리스트.(             새로운 바퀴("전면좌측"), 새로운 바퀴("앞좌우"),             새로운 바퀴("뒷좌석"), 새로운 바퀴("뒷좌우"),             새로운 (), 새로운 엔진()         );     }      @오버라이드     공중의 공허하게 하다 받아들이다(카레멘털바이저 방문자) {         을 위해 (캐리어먼트 원소의 : 요소들) {             원소의.받아들이다(방문자);         }         방문자.방문하다();     } }  계급 카레인트도비터 기구들 카레멘털바이저 {     @오버라이드     공중의 공허하게 하다 방문하다( 보디) {         시스템.밖으로.인쇄하다("내 몸을 움직여");     }      @오버라이드     공중의 공허하게 하다 방문하다(자동차 자동차) {         시스템.밖으로.인쇄하다("내 차 시동");     }      @오버라이드     공중의 공허하게 하다 방문하다(바퀴 바퀴를 돌리다) {         시스템.밖으로.인쇄하다("내게 낄낄" + 바퀴를 돌리다.getName() + "바퀴");     }      @오버라이드     공중의 공허하게 하다 방문하다(엔진 엔진) {         시스템.밖으로.인쇄하다("엔진 시동 중");     } }  계급 CarElementPrintVisitor 기구들 카레멘털바이저 {     @오버라이드     공중의 공허하게 하다 방문하다( 보디) {         시스템.밖으로.인쇄하다("찾아가는 몸");     }      @오버라이드     공중의 공허하게 하다 방문하다(자동차 자동차) {         시스템.밖으로.인쇄하다("방문차");     }      @오버라이드     공중의 공허하게 하다 방문하다(엔진 엔진) {         시스템.밖으로.인쇄하다("방문 엔진");     }      @오버라이드     공중의 공허하게 하다 방문하다(바퀴 바퀴를 돌리다) {         시스템.밖으로.인쇄하다("찾아가는 중" + 바퀴를 돌리다.getName() + "바퀴");     } }  공중의 계급 방문자데모 {     공중의 정태의 공허하게 하다 본래의(최종의 [] 아그) {         자동차 자동차 = 새로운 자동차();          자동차.받아들이다(새로운 CarElementPrintVisitor());         자동차.받아들이다(새로운 카레인트도비터());     } } 


출력

전방 좌측 휠 방문 전방 우측 휠 방문 후방 우측 휠 방문 후방 우측 휠 방문 엔진 방문 전방 좌측 휠 방문 전방 우측 휠 차기 전방 우측 휠 발차기 후방 좌측 휠 발차기 내 차체 이동 내 차량 시동

공통 Lisp 예제

원천

(품위를 손상하다 자동차로 ()   ((요소들 :initarg :elements)))  (품위를 손상하다 자동 부품 ()   ((이름을 붙이다 :initarg :이름 :initform "[car-part]")))  (수법을 어기다 인쇄물. ((p 자동 부품) 물줄기가 흐르다)   (인쇄물. (슬롯 값 p '이름) 물줄기가 흐르다))  (품위를 손상하다 바퀴를 돌리다 (자동 부품) ())  (품위를 손상하다 보디 (자동 부품) ())  (품위를 손상하다 엔진 (자동 부품) ())  (유전적 횡단을 하다 (기능을 발휘하다 이의를 제기하다 다른 목적의))  (수법을 어기다 횡단을 하다 (기능을 발휘하다 (a 자동차로) 다른 목적의)   (무턱대고 (요소들) a     (돌리스트 (e 요소들)       (펑콜 기능을 발휘하다 e 다른 목적의))))  ;;; 무엇인가 하는 방문  ;; 모두 잡아라 (수법을 어기다 하는 일 (이의를 제기하다 다른 목적의)   (형식을 갖추다 t " ~s와 ~s가 어떻게 상호 작용해야 하는지 몰라~%" 이의를 제기하다 다른 목적의))  ;; 휠 및 정수를 포함하는 방문 (수법을 어기다 하는 일 ((이의를 제기하다 바퀴를 돌리다) (다른 목적의 정수의))   (형식을 갖추다 t "바퀴 ~s ~s ~s time~%" 이의를 제기하다 다른 목적의))  ;;; 휠 및 기호를 포함하는 방문 (수법을 어기다 하는 일 ((이의를 제기하다 바퀴를 돌리다) (다른 목적의 심볼))   (형식을 갖추다 t " 기호 ~s~%를 사용하여 ~s를 상징적으로 사용" 이의를 제기하다 다른 목적의))  (수법을 어기다 하는 일 ((이의를 제기하다 엔진) (다른 목적의 정수의))   (형식을 갖추다 t "시동 엔진 ~s ~s배~%" 이의를 제기하다 다른 목적의))  (수법을 어기다 하는 일 ((이의를 제기하다 엔진) (다른 목적의 심볼))   (형식을 갖추다 t "기호 ~s~%를 사용하여 ~s 상징적으로 엔진 시동 이의를 제기하다 다른 목적의))  (하게 하다 ((a (가공의 '자동의                         :elements `(,(가공의 '바퀴' :이름 "전륜-좌륜")                                     ,(가공의 '바퀴' :이름 "앞바퀴-우회전")                                     ,(가공의 '바퀴' :이름 "뒷바퀴")                                     ,(가공의 '바퀴' :이름 "후륜 우측")                                     ,(가공의 '몸' :이름 "몸")                                     ,(가공의 '엔진' :이름 "엔진")))))   ;; 요소 인쇄를 건너뛰기   ;;; stream *standard-output*은 여기서 다른 객체의 역할을 함   (횡단을 하다 #'인쇄하다 a *표준-출력*)    (테리프리의) ;;; 새 줄 인쇄    ;;; 다른 개체로부터 임의의 컨텍스트를 사용하여 이동   (횡단을 하다 #'하는 일 a 42)    ;;; 다른 개체로부터 임의의 컨텍스트를 사용하여 이동   (횡단을 하다 #'하는 일 a 'abc)) 

출력

'전면-좌차륜' '후면-우차륜' '후면-우차륜' '엔진' '전면-우차륜' 42번 '후면-우차륜' 42번 '후면-우차륜' 42번 '후면-우차륜' 42번 '후면-우차륜' 42번 '시동차' 42번 '시동차'는 어떻게 시동엔진 '엔진'과 상호작해야 하는지 모른다.el "전좌차륜"은 기호 ABC 발차륜 "후좌차륜"을 기호 ABC 발차륜 "후좌차륜"을 기호 ABC를 사용하여 "후좌차륜"을 기호 ABC를 사용하여 "신체"와 ABC가 기호를 사용하여 시동엔진 "엔진"을 기호적으로 어떻게 상호 작용해야 하는지 모른다.

메모들

other-object매개 변수가 다음에서 불필요함traverse. 그 이유는 어휘적으로 캡처된 객체로 원하는 표적 방법을 호출하는 익명 함수를 사용할 수 있기 때문이다.

(수법을 어기다 횡단을 하다 (기능을 발휘하다 (a 자동차로)) ;;; 기타 개체가 제거됨   (무턱대고 (요소들) a     (돌리스트 (e 요소들)       (펑콜 기능을 발휘하다 e)))) ;;; 여기도    ;; ...    ;;; 인쇄를 위한 대체 방법   (횡단을 하다 (람다 (o) (인쇄하다 o *표준-출력*)) a)    ;;; 을(를) 사용할 수 있는 방법   ;; a 및 정수 42의 요소   (횡단을 하다 (람다 (o) (하는 일 o 42)) a) 

지금은 익명함수의 본체로부터 발급받은 호출에 복수파견이 일어나기 때문에.traverse단지 매핑 함수로서, 객체 요소 위에 기능 응용프로그램을 분배한다.따라서 두 개의 물체가 관련되었다는 증거가 없는 매핑 기능을 제외한 모든 방문자 패턴의 흔적이 사라진다.두 개의 물체와 그 종류에 대한 디스패치가 있다는 모든 지식은 람다 함수에 있다.

파이톤 예

파이톤은 고전적인 의미에서의 과부하 방법(통과된 파라미터의 종류에 따른 폴리모픽 동작)을 지원하지 않기 때문에, 다른 모델 타입에 대한 "방문" 방법은 다른 이름을 가질 필요가 있다.

원천

""" 방문자 패턴 예제. """  로부터 a b c 수입하다 ABC메타, 추상적인 방법  Not_Implemented = "이걸 실행해야 해."  계급 캐리어먼트:     __metaclass___ = ABC메타     @abstractmethod     반항하다 받아들이다(자아의, 방문자):         높이다 구현되지 않음Error(Not_Implemented)  계급 (캐리어먼트):     반항하다 받아들이다(자아의, 방문자):         방문자.방문바디(자아의)  계급 엔진(캐리어먼트):     반항하다 받아들이다(자아의, 방문자):         방문자.방문엔진(자아의)  계급 바퀴(캐리어먼트):     반항하다 __init___(자아의, 이름을 붙이다):         자아의.이름을 붙이다 = 이름을 붙이다     반항하다 받아들이다(자아의, 방문자):         방문자.방문휠(자아의)  계급 자동차(캐리어먼트):     반항하다 __init___(자아의):         자아의.요소들 = [             바퀴("전면좌측"), 바퀴("앞좌우"),             바퀴("뒷좌석"), 바퀴("뒷좌우"),             (), 엔진()         ]      반항하다 받아들이다(자아의, 방문자):         을 위해 원소의  자아의.요소들:             원소의.받아들이다(방문자)         방문자.방문차(자아의)  계급 카레멘털바이저:     __metaclass___ = ABC메타     @abstractmethod     반항하다 방문바디(자아의, 원소의):         높이다 구현되지 않음Error(Not_Implemented)     @abstractmethod     반항하다 방문엔진(자아의, 원소의):         높이다 구현되지 않음Error(Not_Implemented)     @abstractmethod     반항하다 방문휠(자아의, 원소의):         높이다 구현되지 않음Error(Not_Implemented)     @abstractmethod     반항하다 방문차(자아의, 원소의):         높이다 구현되지 않음Error(Not_Implemented)  계급 카레인트도비터(카레멘털바이저):     반항하다 방문바디(자아의, 보디):         인쇄하다("내 몸을 움직인다.")     반항하다 방문차(자아의, 자동차):         인쇄하다("내 차 시동 걸게.")     반항하다 방문휠(자아의, 바퀴를 돌리다):         인쇄하다("내게 낄낄대.{}바퀴를 돌리다.".형식을 갖추다(바퀴를 돌리다.이름을 붙이다))     반항하다 방문엔진(자아의, 엔진):         인쇄하다("내 엔진 시동 걸게.")  계급 CarElementPrintVisitor(카레멘털바이저):     반항하다 방문바디(자아의, 보디):         인쇄하다("찾아가는 몸.")     반항하다 방문차(자아의, 자동차):         인쇄하다("방문차.")     반항하다 방문휠(자아의, 바퀴를 돌리다):         인쇄하다("찾아가는 중{}바퀴를 돌리다.".형식을 갖추다(바퀴를 돌리다.이름을 붙이다))     반항하다 방문엔진(자아의, 엔진):         인쇄하다("방문 엔진.")  자동차 = 자동차() 자동차.받아들이다(CarElementPrintVisitor()) 자동차.받아들이다(카레인트도비터()) 

출력

방문 프론트 좌측 휠.방문 프론트 우측 휠.왼쪽 뒷바퀴를 방문 중.우측 뒷바퀴를 방문 중.방문단.방문 엔진.방문차.내 왼쪽 앞바퀴를 차고 있어.내 오른쪽 앞바퀴를 차고 있어.내 뒷바퀴를 차고 있어.오른쪽 뒷바퀴를 차고 있어.내 몸을 움직인다.엔진 시동 중.내 차에 시동을 거는 중. 

추상화

Python 3 이상을 사용하는 경우, 다음과 같은 수용 방법을 일반적으로 구현할 수 있다.

계급 방문가능:     반항하다 받아들이다(자아의, 방문자):         찾다 = "visit_" + 타자를 치다(자아의).__qualname___.대체하다(".", "_")         돌아오다 게트레질하다(방문자, 찾다)(자아의) 

이미 시행된 수업에서 후퇴하고 싶다면 수업의 방법 결정 명령을 반복하는 것으로 이를 확대할 수 있다.또한 하위 클래스 후크 기능을 사용하여 조회를 미리 정의할 수도 있다.

관련 설계 패턴

  • 반복기 패턴 – 통과된 객체 내에서 유형 분류를 하지 않고 방문자 패턴과 같은 통과 원리를 정의함
  • 교회 인코딩 – 기능 프로그래밍에서 태그가 지정된 조합/섬 유형을 그러한 유형에 대한 "방문자"의 행동을 사용하여 모델링할 수 있으며, 방문자 패턴이 변형과 패턴을 모방할 수 있도록 한다.

참고 항목

참조

  1. ^ a b Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 331ff. ISBN 0-201-63361-2.{{cite book}}: CS1 maint : 복수이름 : 작성자 목록(링크)
  2. ^ 방문자 패턴 실제 사례
  3. ^ a b c d Budd, Timothy (1997). An introduction to object-oriented programming (2nd ed.). Reading, Mass.: Addison-Wesley. ISBN 0-201-82419-1. OCLC 34788238.
  4. ^ "The Visitor design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  5. ^ Reddy, Martin (2011). API design for C++. Boston: Morgan Kaufmann. ISBN 978-0-12-385004-1. OCLC 704559821.

외부 링크