如何更改此设计以避免下降? [英] How to change this design to avoid a downcast?

查看:113
本文介绍了如何更改此设计以避免下降?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一些对象的集合,这些对象都从基类继承。像...

 抽象公共类动物
{

}

公共类狗:动物
{

}

类猴子:动物
{

}现在,我们需要喂养这些动物,但不允许他们知道如何喂养自己。如果可以,答案将很简单:

  foreach(myAnimals中的Animal a)
{
a.feed();
}

但是,他们不知道如何养活自己,所以我们想做这样的事情:

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

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

foreach(myAnimals中的动物a)
{
Program.FeedAnimal(a);
}
}

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

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

}

当然,这不会编译,因为这将迫使向下转换。



感觉好像有一种设计模式或其他一些使用泛型的解决方案可以帮助我解决这个问题,但我还没有对此进行任何尝试。 / p>

建议?

解决方案

在Linq习语中使用的选中的下垂式,是绝对安全的。

  foreach(myAnimals.OfType< Monkey>()中的猴子m)
Program.FeedAnimal (m);

或者您可以使用访客模式。访客对象知道所有类型的动物,因此每种对象都有一个 FeedAnimal 函数。您将访问者对象传递给动物的 Feed 函数,然后回调正确的 FeedAnimal 方法,并传递 this



要使其具有可扩展性,您需要一个 Dictionary 动物饲养者的数量:

 私有静态Dictionary< Type,Action< Animal> _feeders; 

要注册喂食动作,您首先需要这样做:

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

//以某种方式给食物
};

但是有些沮丧,您还必须在密钥中输入正确的类型。所以做一个助手:

  public static void AddFeeder< TAnimal>(Action< TAnimal> feeder)其中TAnimal:动物
{
_feeders [typeof(TAnimal)] = a => feeder((TAnimal)a);
}

这捕获了模式,因此您可以完全安全地重用它:

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

然后,当您需要喂养动物时,请使用以下扩展方法:

  public static void Feed(此动物a)
{
_feeders [a.GetType()](a);
}

例如

  a.Feed(); 

因此_feeders是与扩展方法相同的静态类中的私有静态字段, AddFeeder 方法。



更新:所有代码都放在一个地方,也有支持

 公共静态类AnimalFeeding 
{
private static Dictionary< Type,Action< Animal> > _feeders
= new Dictionary< Type,Action< Animal>>();

公共静态无效AddFeeder< TAnimal>(动作< TAnimal> Feeder)
其中,TAnimal:动物
{
_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>馈线
if(_feeders.TryGetValue(t,out feeder))
{
feeder(a);
的回报;
}
}

抛出新的SystemException(找不到供稿者 + a.GetType());
}
}

您还可以通过以下接口循环访问每种类型 t -基本上是相同的想法,因此我在此处简化了示例。


Let's say I have a collection of objects that all inherit from a base class. Something like...

   abstract public class Animal
    {

    }

    public class Dog :Animal
    {

    }

    class Monkey : Animal
    {

    }

Now, we need to feed these animals, but they are not allowed to know how to feed themselves. If they could, the answer would be straightforward:

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

However, they can't know how to feed themselves, so we want to do something like this:

    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.");
    }

}

Of course, this won't compile, as it would be forcing a downcast.

It feels as if there's a design pattern or some other solution with generics that help me out of this problem, but I haven't put my fingers on it yet.

Suggestions?

解决方案

A checked downcast, as used within Linq idioms, is perfectly safe.

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

Or you could use the visitor pattern. The visitor object knows all the types of animal, so it has a FeedAnimal function for each. You pass the visitor object to an animal's Feed function, and it calls back to the correct FeedAnimal method, passing this.

To make it extensible, you need a Dictionary of animal feeders:

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

To register a feeding action, you'd do this to begin with:

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

        // give food to m somehow
    };

But there's a downcast, and you have to give the correct type in the key as well. So make a helper:

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

This captures the pattern so you can reuse it completely safely:

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

Then when you need to feed an animal, use this extension method:

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

e.g.

a.Feed();

So _feeders would be a private static field in the same static class as the extension method, along with the AddFeeder method.

Update: all the code in one place, also with support for inheritance:

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());
    }
}

You could also have a loop through the interfaces support by each type t - basically the same idea so I've kept the example simple here.

这篇关于如何更改此设计以避免下降?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆