Delphi의 방문자 모드는 기본 방문자 모드를 확장합니다. 방문자 모드에 대한 자세한 내용은 [Gam+, 페이지 331..344]를 참조하세요.
개체 구조의 중립화된 요소에 대해 작동하는 작업을 나타냅니다. 클래스를 변경하지 않고도 각 요소에 대해 작동하는 새 작업을 정의할 수 있습니다.
[Gam+,331페이지].
모델을 클래스와 클래스 멤버로 표현하는 'Rational Rose, ModelMaker'와 같은 객체지향 모델링 도구를 생각해 보세요.
모델링 도구는 클래스의 모든 멤버 나열, 클래스의 코드 프레임워크 생성, 리버스 엔지니어링 등과 같은 멤버 운영을 위한 다양한 기능을 제공합니다.
이러한 작업의 대부분은 서로 다른 멤버에 대해 서로 다른 작업을 수행합니다. 멤버를 필드, 메소드,
속성(속성). 따라서 필드를 구체적으로 처리하는 클래스, 메서드를 구체적으로 처리하는 클래스 등을 만들어야 합니다. 물론 멤버 클래스 컬렉션은 컴파일되는 언어에 따라 다릅니다. 그러나 특정 언어에 대해서는 많은 변화가 없습니다.
그림은 일부 멤버 클래스의 프레임워크를 보여줍니다. 문제가 발생합니다. 이 모든 작업을 다른 멤버 클래스에 분산하면
이는 전체 시스템을 이해하고, 수정하고, 유지하기 어렵게 만듭니다. 클래스 코드 생성과 클래스 멤버 확인을 함께 사용하면 혼란이 발생합니다. 일부 클래스는 새 작업을 추가할 때 다시 컴파일해야 합니다(적어도 모든 관련 시스템도 다시 컴파일해야 함). 방법이 있습니다. 멤버 클래스와 독립적으로 새 작업을 수행하는 작업으로 추가할 수 있습니다.
위의 두 가지 목표를 달성하기 위해 각 클래스의 관련 작업을 독립 개체( visitor 라고 함)로 래핑할 수 있습니다.
그리고 클래스 멤버 목록을 반복할 때 이 객체를 현재 멤버에 전달합니다. 회원이 액세스를 '수락'하면 회원은 자신의 정보가 포함된 요청을 방문자에게 보냅니다. 멤버는 자신을 매개변수로 사용합니다. 방문자는 이러한 작업을 수행합니다.
예를 들어 방문자를 사용하지 않는 코드 생성기는 멤버 클래스 TMember.WriteInterfaceCode(Output: TStream)의 추상 메서드를 통해 코드를 생성할 수 있습니다. 각 멤버는 WriteInterfaceCode를 호출하여 적절한 출력 코드를 생성합니다. 방문자를 통해 코드가 생성되면 TinterfaceCodeVisitor 개체가 생성되고 매개 변수가 액세스 개체인 AcceptVisitor 메서드가 구성원 목록에서 호출됩니다. AcceptVisitor를 구현하는 각 구성원은 방문자를 콜백합니다 . 필드는 방문자의 VisitField 메서드를 호출하고 메서드는 VisitMethod 메서드를 호출합니다. 이러한 방식으로 이전 클래스 Tfield의 WriteInterfaceCode 작업은 이제 TinterfaceCodeVisitor의 VisitField 작업이 됩니다.
방문자가 단순한 코드 생성 이상의 작업을 수행하도록 하려면 모든 구성원 목록 방문자가 추상 상위 클래스 TmemberVisitor를 가져야 합니다. TmemberVisitor는 각 구성원에 대한 메소드를 정의해야 합니다. 멤버를 HTML 형식으로 출력해야 하는 애플리케이션은 TmemberVisitor의 새로운 하위 클래스를 정의하며 더 이상 멤버 클래스에 애플리케이션별 코드를 추가할 필요가 없습니다. 방문자 패턴은 관련 방문자의 각 작업을 캡슐화합니다.
방문자 패턴을 사용하면 두 가지 수준의 클래스를 정의해야 합니다. 하나는 작업을 허용하는 요소에 해당하고(Tmember 수준), 다른 하나는 요소에 대한 작업에 대해 정의됩니다(TmemberVisitor 수준). 새 작업을 추가할 때 방문자 계층 구조에 새 하위 클래스를 추가하기만 하면 됩니다. 새로운 기능을 추가하기 위해 새로운 TmemberVisitor 하위 클래스를 정의하기만 하면 됩니다.
다음 코드는 위에서 설명한 Tmember 클래스의 방문자 패턴 적용을 보여줍니다.
유형
TMember = 클래스(TObject)
공공의
절차 AcceptMemberVisitor(방문자: TMemberVisitor);
끝;
TField = 클래스(TMember)
공공의
절차 AcceptMemberVisitor(방문자: TMemberVisitor);
끝;
TMethod = 클래스(TMember)
공공의
절차 AcceptMemberVisitor(방문자: TMemberVisitor);
끝;
TProperty = 클래스(Tmember)
공공의
절차 AcceptMemberVisitor(방문자: TMemberVisitor);
끝;
TMemberVisitor = 클래스(TObject)
공공의
절차 VisitField(인스턴스: TField);
절차 VisitMember(인스턴스: TMember);
절차 VisitMethod(인스턴스: TMethod);
절차 VisitProperty(인스턴스: TProperty);
끝;
구현
{T멤버}
시작하다
Visitor.VisitMember(Self);
끝;
{TField}
절차 TField.AcceptMemberVisitor(방문자: TMemberVisitor);
시작하다
끝;
{ T방법 }
절차 TMethod.AcceptMemberVisitor(방문자: TMemberVisitor);
시작하다
Visitor.VisitMethod(Self);
끝;
{TPProperty}
절차 TProperty.AcceptMemberVisitor(방문자: TMemberVisitor);
시작하다
Visitor.VisitProperty(Self);
끝;
{ TMemberVisitor }
절차 TMemberVisitor.VisitField(인스턴스: TField);
시작하다
끝;
프로시저 TMemberVisitor.VisitMember(인스턴스: TMember);
시작하다
끝;
절차 TMemberVisitor.VisitMethod(인스턴스: TMethod);
시작하다
끝;
절차 TMemberVisitor.VisitProperty(인스턴스: TProperty);
시작하다
끝;
설명하다:
? TMember, TField, TMethod 및 Tproperty는 모두 AcceptMemberVisitor 메서드를 구현합니다.
? TMemberVisitor 클래스는 VisitMember, VisitField 및 기타 메소드를 구현합니다. TmemberVisitor는 추상 클래스이며 모든 메소드는 구체적인 하위 클래스에 의해 구현됩니다.
다음은 간단한 코드 생성기의 구현입니다.
코드 소개:
• TCodeGenerationVisitor는 멤버를 구현하는 코드 생성기에 대한 방문자입니다.
? 방문자는 상황에 맞는 속성을 정의합니다. 출력: TTextStream,
? VisitXXX가 호출되기 전에 설정해야 합니다. 예: DrawingVisitor에는 일반적으로 그리기 작업을 지원하기 위한 캔버스가 포함된 컨텍스트가 필요합니다. 전체 멤버 쌍을 반복하기 전에 컨텍스트가 코드 생성기에 할당됩니다.
? 코드 생성기는 생성된 클래스에 대한 모든 코드를 통합합니다.
방문자 모드를 실제로 이해하려면 이 예제를 실행하고 이중 디스패치 메커니즘(수락/방문)을 더 자세히 알아볼 수 있습니다.
단위 CodeGenerator;
인터페이스
클래스, TextStream을 사용합니다.
유형
TCodeGenerator = 클래스(TObject)
공공의
프로시저 생성(멤버: TList; 출력: TTextStream);
끝;
구현
회원을 사용합니다.
유형
TCodeGenerationVisitor = 클래스(TMemberVisitor)
사적인
FOutput: TTextStream;
공공의
절차 VisitField(인스턴스: TField);
절차 VisitMethod(인스턴스: TMethod);
절차 VisitProperty(인스턴스: TProperty);
속성 출력: TTextStream 읽기 FOutput 쓰기 FOutput;
끝;
{TCodeGenerationVisitor}
절차 TCodeGenerationVisitor.VisitField(인스턴스: TField);
시작하다
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
끝;
절차 TCodeGenerationVisitor.VisitMethod(인스턴스: TMethod);
var
MKStr, DTStr: 문자열;
시작하다
사례 Instance.MethodKind of
mkConstructor: MKStr := '생성자';
mkDestructor: MKStr := '소멸자';
mkProcedure: MKStr := '절차';
mkFuntion: MKStr := '함수';
끝;
Instance.MethodKind = mkFunction이면
DTStr := ': ' + Instance.DataName
또 다른
DTStr := ';
{코드가 불완전합니다. Tmethod 코드 생성을 보여주기에 충분합니다.}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr, 인스턴스.이름, 인스턴스.매개변수, DTStr]);
끝;
절차 TCodeGenerationVisitor.VisitProperty(인스턴스: TProperty);
시작하다
Output.WriteLnFmt(' 속성 %s: %s 읽기 %s 쓰기 %s;',
[인스턴스.이름, 인스턴스.데이터이름,
Instance.ReadSpecifier, Instance.WriteSpecifier]);
끝;
{TCodeGenerator}
절차 TCodeGenerator.Generate(멤버: TList; 출력: TTextStream);
var
I: 정수;
시작하다
{클래스 정의 작성}
Output.WriteLine('TSample = 클래스(TObject)');
{좋은! 코드 생성기에 참여한 방문자}
방문자 := TCodeGenerationVisitor.Create;
노력하다
{VisitXXX 방법에 더 잘 액세스하려면 모든 방문에 대한 컨텍스트를 제공해야 합니다. }
for I := 0 ~ Members.Count - 1 do
{좋은 일이 발생한 특정 코드 섹션}
TMember(구성원[I]).AcceptMemberVisitor(방문자);
마지막으로
방문객.무료;
끝;
{반원 코드 생성 완료}
Output.WriteLine('end;');
끝;
정리
//"디자인 패턴"에서 많은 부분 발췌,