3

基本クラスからすべて継承するオブジェクトのコレクションがあるとします。何かのようなもの...

   abstract public class Animal
    {

    }

    public class Dog :Animal
    {

    }

    class Monkey : Animal
    {

    }

今、私たちはこれらの動物に餌をやる必要がありますが、彼らは自分自身を養う方法を知ることは許されていません. 可能であれば、答えは簡単です。

foreach( Animal a in myAnimals )
{
   a.feed();
}

しかし、彼らは自分自身を養う方法を知ることができないので、次のようなことをしたいと考えています:

    class Program
{
    static void Main(string[] args)
    {
        List<Animal> myAnimals = new List<Animal>();

        myAnimals.Add(new Monkey());
        myAnimals.Add(new Dog());

        foreach (Animal a in myAnimals)
        {
            Program.FeedAnimal(a);
        }
    }

    void FeedAnimal(Monkey m) {
        Console.WriteLine("Fed a monkey.");
    }

    void FeedAnimal(Dog d)
    {
        Console.WriteLine("Fed a dog.");
    }

}

もちろん、ダウンキャストを強制するため、これはコンパイルされません。

この問題を解決するのに役立つジェネリックを使用した設計パターンまたはその他のソリューションがあるように感じますが、まだ指を置いていません。

提案?

4

6 に答える 6

8

Linq のイディオムで使用されるチェック ダウンキャストは、完全に安全です。

foreach (Monkey m in myAnimals.OfType<Monkey>())
    Program.FeedAnimal(m);

または、ビジター パターンを使用することもできます。FeedAnimal訪問者オブジェクトは動物のすべての種類を認識しているため、それぞれに対応する機能を持っています。ビジター オブジェクトを動物の関数に渡すと、正しいメソッドにFeedコールバックされ、が渡されます。FeedAnimalthis

Dictionary拡張可能にするには、次の動物フィーダーが必要です。

private static Dictionary<Type, Action<Animal>> _feeders;

フィード アクションを登録するには、まず次のようにします。

_feeders[typeof(Monkey)] = 
    a =>
    {
        Monkey m = (Monkey)a;

        // give food to m somehow
    };

しかし、ダウンキャストがあり、キーにも正しいタイプを指定する必要があります。したがって、ヘルパーを作成します。

public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) where TAnimal : Animal
{
    _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
}

これによりパターンがキャプチャされるため、完全に安全に再利用できます。

AddFeeder<Monkey>(monkey => GiveBananasTo(monkey));
AddFeeder<Dog>(dog => ThrowBiscuitsAt(dog));

次に、動物に餌をやる必要がある場合は、次の拡張メソッドを使用します。

public static void Feed(this Animal a)
{
    _feeders[a.GetType()](a);
}

例えば

a.Feed();

したがって、_feeders は、メソッドとともに、拡張メソッドと同じ静的クラスのプライベート静的フィールドになりますAddFeeder

更新:すべてのコードが 1 か所にあり、継承もサポートされています。

public static class AnimalFeeding
{
    private static Dictionary<Type, Action<Animal>> _feeders 
        = new Dictionary<Type, Action<Animal>>();

    public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) 
        where TAnimal : Animal
    {
        _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
    }

    public static void Feed(this Animal a)
    {
        for (Type t = a.GetType(); t != null; t = t.BaseType)
        {
            Action<Animal> feeder;
            if (_feeders.TryGetValue(t, out feeder))
            {
                feeder(a);
                return;
            }
        }

        throw new SystemException("No feeder found for " + a.GetType());
    }
}

また、各タイプでサポートされているインターフェイスをループすることもできますt。基本的には同じ考え方なので、ここでは例を単純にしています。

于 2009-03-22T00:41:34.293 に答える
3

これは OOD の古典的な問題です。クラス (動物) のセットが固定されている場合は、訪問者パターンを使用できます。アクションのセット (例: フィード) が制限されている場合は、メソッド feed() を Animal に追加するだけです。これがどれも当てはまらない場合、簡単な解決策はありません。

于 2009-03-22T00:42:09.097 に答える
3

まず、オブジェクト指向設計の主なポイントの 1 つは、オブジェクトがデータをそのデータに作用する動作にバンドルすることです (つまり、「動物は自分自身を養う方法を知っている」)。つまり、これは「先生、これをすると痛いです!だから、それをしないでください」という状況の 1 つです。

そうは言っても、あなたが説明した以上の話があり、「適切な」OODを実行できないのには十分な理由があると確信しています. したがって、いくつかのオプションがあります。

FeedAnimal(Animal a) メソッドでリフレクションを使用して動物の種類を見つけることができます。基本的に、FeedAnimal メソッドでポリモーフィズムを行っています。

static void FeedAnimal(Animal a)
{
    if (a is Dog)
    {
        Console.WriteLine("Fed a dog.");
    }
    else if (a is Monkey)
    {
        Console.WriteLine("Fed a monkey.");
    }
    else
    {
        Console.WriteLine("I don't know how to feed a " + a.GetType().Name + ".");
    }      
}

よりオブジェクト指向ですが、より複雑な方法は、他の人が提案した Visitor パターンを使用することです。これは、経験豊富な開発者にとってはより洗練されたものですが、初心者のプログラマーにとっては明白ではなく、読みやすいことはほぼ間違いありません。どちらのアプローチを好むかは、所有している動物の種類によって異なります。

于 2009-03-22T01:10:57.530 に答える
1

ジェネリックは、「犬のリスト」を持っていて、「動物であるもののリスト」(つまり ) である引数を使用してメソッドを呼び出したい場合にのみ役立ちますList<T> where T : Animal-ここでは役に立たないと思います。

訪問者パターンが必要になると思います...特定の種類の動物に餌をやる方法を知っている可能性のあるオブジェクトのセットで、方法を知っているものが見つかるまでそれらを試し続けます...

于 2009-03-22T00:43:03.680 に答える
0

動物は自分自身や他の動物に餌を与えることが許可されていないため、動物に餌をやるプライベートな方法を所有するクラス「所有者」、「飼育係」、または「世話人」を作成する以外に選択肢はありません。

于 2009-03-22T00:42:06.163 に答える
0

動物の種類を識別するメンバー変数を動物クラスに含めてから、feed 関数にそれを読み取らせ、それに基づいて異なる結果を生成させることができます。

于 2009-03-22T00:45:43.860 に答える