8

以下がビジターパターンの許容可能な使用法であるかどうかを知りたいです。Accept()またはVisit()呼び出しから戻るのは少し不快に感じます-これはこのパターンの適切な使用法ですか?そうでない場合は、なぜですか?

注:長いコードサンプルについてお詫びします。訪問者は常に少し関与しているように見えるので、私が行っていることを理解するために必要なようです...

interface IAnimalElement<T>
{
   T Accept(IAnimalVisitor<T> visitor);
}

interface IAnimalVisitor<T>
{
    T Visit(Lion lion);
    T Visit(Peacock peacock);
    T VisitZoo(List<Animal> animals);
}

abstract class Animal
{
    public int Age { get; protected set; }
}

class Lion : Animal, IAnimalElement<int>
{
    public Lion(int age)
    {
        Age = age;
    }

    public int Accept(IAnimalVisitor<int> visitor)
    {
        return visitor.Visit(this);
    }
}

class Peacock : Animal, IAnimalElement<int>
{
    public Peacock(int age)
    {
        Age = age;
    }

    public int Accept(IAnimalVisitor<int> visitor)
    {
        return visitor.Visit(this);
    }
}

class AnimalAgeVisitor : IAnimalVisitor<int>
{
    public int TotalAge { get; private set; }

    int IAnimalVisitor<int>.Visit(Lion lion)
    {
        TotalAge += lion.Age;
        return lion.Age;
    }

    int IAnimalVisitor<int>.Visit(Peacock peacock)
    {
        TotalAge += peacock.Age + 10;
        return peacock.Age + 10; // peacocks ages are always -10y, correct.
    }

    public int VisitZoo(List<Animal> animals)
    {
        // Calculate average animal age.

        int sum = 0;
        int count = 0;
        foreach (IAnimalElement<int> animal in animals)
        {
            sum += animal.Accept(this);
            ++count;
        }

        return count == 0 ? 0 : sum / count;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Animal> animals = new List<Animal>() { new Lion(10), 
          new Lion(15), new Peacock(3), new Lion(2), new Peacock(9) };

        AnimalAgeVisitor visitor = new AnimalAgeVisitor();

        Console.WriteLine("Average age = {0}, Total age = {1}", 
            visitor.VisitZoo(animals), visitor.TotalAge);
    }
}
4

4 に答える 4

4

私には、これは実装が少し先延ばしにされているように感じます。

Visit および Accept メソッドが void を返すようにし、Visitor オブジェクトのすべての状態を追跡します。最後に質問します。

また ...

Visit と Accept に進行中の状態を返させ、着信中の進行中の状態を機能的な方法で受け入れさせます。

2 番目のオプションを選択する場合、ビジター オブジェクトまたはパターンが必要かどうかはよくわかりませんが、代わりに反復子、関数、およびいくつかの一時的な状態を使用できます。

于 2009-02-04T05:09:47.240 に答える
3

簡単な回答:ジェネリック パラメーターを返す IVisitor を公開しても問題はないと思います。FxCop ルール
を参照してください。

次に、それぞれが異なる値を返す異なるIVisitorの使用を許可します。

ただし、あなたの場合、 Visitor は役に立ちません。すべての動物にはAgeプロパティがあるため、Animalまたは新しいIAnimalインターフェイスですべてを実行できます。

代わりに、 Strong Typingを失うという犠牲を払って複数のディスパッチを使用しています。

次のようなスイッチを置き換えたい (または書きたくない) 場合は、ビジターパターンを使用します。

IAnimal animal = ...;
switch (animal.GetType().Name)
{
  case "Peacock":
    var peacock = animal as Peacock;
    // Do something using the specific methods/properties of Peacock
    break;
  case "Lion":
    var peacock = animal as Lion;
    // Do something using the specific methods/properties of Lion
    break;
   etc...
}

またはネストされたif-then-else に相当します。

その目的は、ポリモーフィズムを使用してインスタンスをそのタイプに関連するルーチンにルーティングし、醜い if-then-else/switch ステートメント手動キャストを回避することです。さらに、無関係なコード間の結合を減らすのに役立ちます。

その代わりに、アクセスするクラス ツリーに仮想メソッドを追加します。ただし、それが不可能または望ましくない場合もあります。

  • 訪問可能なクラスコードは変更できません(たとえば、所有されていません)
  • 訪問コードとは関係のない訪問可能なクラス コード(クラスに追加すると、クラスの結束が低下します)。

そのため、オブジェクト ツリー (html ノード、レクサー トークンなど) をトラバースするためによく使用されます。訪問者パターンは、次のインターフェースを意味します。

  • IVビジター

    /// <summary>
    /// Interface to implement for classes visiting others. 
    /// See Visitor design pattern for more details.
    /// </summary>
    /// <typeparam name="TVisited">The type of the visited.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public interface IVisitor<TVisited, TResult> : IVisitor where TVisited : IVisitable
    {
        TResult Visit(TVisited visited);
    }
    
    /// <summary>
    /// Marking interface.
    /// </summary>
    public interface IVisitor{}
    
  • IVisitable

    /// <summary>
    /// Interface to implement for classes visitable by a visitor.
    /// See Visitor design pattern for more details.
    /// </summary>
    /// <typeparam name="TVisitor">The type of the visitor.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public interface IVisitable<TVisitor, TResult> : IVisitable where TVisitor : IVisitor
    {
        TResult Accept(TVisitor visitor);
    }
    
    /// <summary>
    /// Marking interface.
    /// </summary>
    public interface IVisitable {}
    

IVisitableでのAcceptの実装は、Visit(this)を呼び出す必要があります。

于 2015-12-23T18:23:38.507 に答える
2

訪問可能な accept メソッドは何も返さないはずです。受け入れは、訪問後または訪問中に何を訪問するかを訪問者に示すことのみを想定しています。

于 2011-05-02T18:38:02.767 に答える
1

それはかなり一般的です。C#で実行できるかどうかはわかりませんが、JavaではAcceptメソッドをジェネリックのままにしておくのが普通なので、返されるものは訪問者ではなく訪問者によって決定されます。

interface IAnimalElement
{
   <T> T Accept(IAnimalVisitor<T> visitor);
}


interface IAnimalVisitor<T> 
{
   T Visit(Peacock animal);
  ...
}

手続きはIAnimalVisitor<Void>返品nullが可能です。

于 2009-02-03T18:51:44.117 に答える