Delphi の訪問者モードは、基本的な訪問者モードを拡張したものです。ビジター モードの詳細については、[Gam+、331 ~ 344 ページ] を参照してください。
オブジェクト構造の中立化された要素を操作する操作を表します。これにより、クラスを変更せずに各要素に作用する新しい操作を定義できます。
[Gam+、331 ページ]。
モデルをクラスおよびクラス メンバーとして表す「Rational Rose、ModelMaker」などのオブジェクト指向モデリング ツールを考えてみましょう。
モデリング ツールは、クラスのすべてのメンバーのリスト、クラスのコード フレームワークの生成、リバース エンジニアリングなど、メンバーを操作するための多くの機能を提供します。
これらの操作のほとんどは、異なるメンバーに対して異なる操作を実行します。メンバーをフィールド、メソッド、
プロパティ (プロパティ)。したがって、フィールドを特別に処理するクラス、メソッドを特別に処理するクラスなどを作成する必要があります。メンバー クラスのコレクションは、当然ながらコンパイルされる言語に依存します。ただし、特定の言語では大きな変化はありません。
この図は、いくつかのメンバー クラスのフレームワークを示しています。これらすべての操作を異なるメンバークラスに分散すると、問題が発生します。
これにより、システム全体の理解、変更、保守が困難になります。クラス コードの生成とクラス メンバーのチェックを組み合わせると、混乱が生じます。一部のクラスは、新しい操作を追加するときに再コンパイルする必要があります (少なくとも関連するすべてのシステムも同様に再コンパイルする必要があります)。方法はあります。メンバー クラスに作用する操作として、メンバー クラスとは独立して新しい操作を追加することができます。
上記 2 つの目標を達成するには、各クラスの関連する操作を独立したオブジェクト ( Visitorと呼ばれる) にラップします。
そして、クラス メンバー リストを反復処理するときに、このオブジェクトを現在のメンバーに渡します。メンバーがアクセスを「受け入れる」と、メンバーは自身の情報を含むリクエストを訪問者に送信します。メンバーはそれ自体をパラメーターとして受け取ります。訪問者はこれらのアクションを実行します。
たとえば、ビジターを使用しないコード ジェネレーターは、メンバー クラスの抽象メソッド TMember.WriteInterfaceCode(Output: TStream) を通じてコードを生成できます。各メンバーは WriteInterfaceCode を呼び出して、適切な出力コードを生成します。コードが訪問者を通じて生成される場合、TinterfaceCodeVisitor オブジェクトが作成され、アクセス オブジェクトであるパラメーターを持つ AcceptVisitor メソッドがメンバー リストで呼び出されます。 AcceptVisitor を実装する各メンバーは訪問者をコールバックします。フィールドは訪問者の VisitField メソッドを呼び出し、メソッドは VisitMethod メソッドを呼び出します。このようにして、以前のクラス Tfield の WriteInterfaceCode オペレーションは、TinterfaceCodeVisitor の VisitField オペレーションになります。
訪問者に単なるコード生成以上のことを実行させるには、すべてのメンバー リストの訪問者が抽象親クラス TmemberVisitor を持つ必要があります。 TmemberVisitor は各メンバーのメソッドを定義する必要があります。メンバーを HTML 形式に出力する必要があるアプリケーションは、TmemberVisitor の新しいサブクラスを定義するため、アプリケーション固有のコードをメンバー クラスに追加する必要がなくなりました。 Visitor パターンは、関連する Visitor 内の各操作をカプセル化します。
Visitor パターンを使用する場合、2 つのレベルのクラスを定義する必要があります。1 つは操作を受け入れる要素に対応するもの (Tmember レベル)、もう 1 つは要素に対する操作用に定義されたもの (TmemberVisitor レベル) です。新しい操作を追加するときは、新しいサブクラスを訪問者階層に追加するだけです。新しい TmemberVisitor サブクラスを定義して、新しい機能を追加するだけかもしれません。
次のコードは、上で説明したクラス Tmember の Visitor パターンの適用を示しています。
タイプ
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);
終わり;
実装
{メンバー}
始める
Visitor.VisitMember(自分);
終わり;
{TFフィールド}
プロシージャ TField.AcceptMemberVisitor(訪問者: TMemberVisitor);
始める
終わり;
{Tメソッド}
プロシージャ TMethod.AcceptMemberVisitor(訪問者: TMemberVisitor);
始める
Visitor.VisitMethod(Self);
終わり;
{TPプロパティ}
プロシージャ 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 は通常、描画操作をサポートするためにキャンバスを含むコンテキストを必要とします。コンテキストは、メンバー ペア全体を反復処理する前に、コード ジェネレーターに割り当てられます。
? コード ジェネレーターは、生成されたクラスのすべてのコードを統合します。
Visitor モードを実際に理解するには、この例を実行し、二重ディスパッチ メカニズム (accept/visit) をさらに学習します。
ユニットコードジェネレーター。
インタフェース
クラス、TextStream を使用します。
タイプ
TCodeGenerator = クラス (TObject)
公共
プロシージャ Generate(メンバー: TList; 出力: TTextStream);
終わり;
実装
メンバーを使用します。
タイプ
TCodeGenerationVisitor = クラス (TMemberVisitor)
プライベート
F出力: 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);
変数
MKStr、DTStr: 文字列;
始める
case Instance.MethodKind of
mkConstructor: MKStr := 'コンストラクター';
mkDestructor: MKStr := 'デストラクター';
mkProcedure: MKStr := 'プロシージャ';
mkFuntion: MKStr := '関数';
終わり;
Instance.MethodKind = mkFunction の場合
DTStr := ': ' + インスタンス.データ名
それ以外
DTStr := ';
{コードは不完全です。Tmethod コード生成を示すには十分です}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr、Instance.Name、Instance.Parameters、DTStr]);
終わり;
プロシージャ TCodeGenerationVisitor.VisitProperty(インスタンス: TProperty);
始める
Output.WriteLnFmt(' プロパティ %s: %s 読み取り %s 書き込み %s;',
[インスタンス.名、インスタンス.データ名、
Instance.ReadSpecifier、Instance.WriteSpecifier]);
終わり;
{Tコードジェネレーター}
プロシージャ TCodeGenerator.Generate(メンバー: TList; 出力: TTextStream);
変数
I: 整数。
始める
{クラス定義の書き込み}
Output.WriteLine('TSample = クラス (TObject)');
{良い! コードジェネレーターに参加した訪問者}
訪問者 := TCodeGenerationVisitor.Create;
試す
{VisitXXX メソッドへのアクセスを向上させるために、すべての訪問にコンテキストを忘れずに提供してください。 }
for I := 0 から Members.Count - 1 まで
{何か良いことが起こったコードの特定のセクション}
TMember(メンバー[I]).AcceptMemberVisitor(訪問者);
ついに
訪問者。無料。
終わり;
{クラスメンバーのコード生成が完了しました}
Output.WriteLine('end;');
終わり;
整理する
//「デザインパターン」からの抜粋多数、