English 中文(简体)
如何改变这个设计以避免沮丧吗?
原标题:
  • 时间:2009-03-22 00:37:26
  •  标签:

让年代说我有一个对象集合,所有从基类继承。之类的……

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

}

当然,这不会编译,因为它会迫使哭笑不得。

感觉好像有一个设计模式或其他解决方案与仿制药,帮我出这个问题,但是我还没把我的手指放在它。

建议吗?

问题回答

检查沮丧,Linq中使用习语,是完全安全的。

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

或者你可以使用访问者模式。访问者对象知道所有类型的动物,所以它有一个<代码> FeedAnimal > < /代码为每个函数。你将访问者对象传递给一个动物s <代码>饲料> < /代码功能,它调用回正确的<代码> FeedAnimal > < /代码方法,通过<代码> < /代码>。

可扩展的,您需要一个<代码>动物饲养者的字典> < /代码:

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

注册一个行动,你d开始:

_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 > < /代码的方法。

<强>更新:< /强>的所有代码在一个地方,也支持继承:

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 <代码> < /代码>——基本上同样的想法,所以我一直以来这里的简单例子。

这是一个在OOD经典问题。如果你的组类(动物)是固定的,您可以使用< a href = " http://en.wikipedia.org/wiki/Visitor_pattern " rel = " nofollow noreferrer " > < / >访问者模式。如果你的行为(如。提要)是有限的,你只需要添加一个饲料()方法。如果这一切都成立,没有简单的解决方案。

首先,面向对象设计的要点之一是对象包数据的行为作用于数据(即。“动物知道如何养活自己”)。这是一个“医生,这很伤我的心,当我这样做!——不要这样做”的情况。

说,我肯定有比你已经描述的故事,你有充分的理由不能够OOD“适当的”。所以你有几个选择。

你可以有你的FeedAnimal(动物)方法使用反射来发现动物的类型。基本上你做你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 + ".");
    }      
}

更面向对象,但更复杂的方法是使用访问者模式建议。这是经验丰富的开发人员更优雅,但可以说不太明显的和可读的更多的新手程序员。你喜欢哪种方法可能取决于你有多少不同的动物类型。

仿制药只会有帮助如果你有一个“狗”列表和想调用一个方法,论证“列表的东西动物”(即<代码>列表< T > T:动物> < /代码),我不认为在这里帮助。

我怀疑你会需要一个< a href = " http://en.wikipedia.org/wiki/Visitor_pattern " rel = " nofollow noreferrer " >访问者模式< / >…一些组对象,< em > < / em >可以知道如何养活一个给定类型的动物,他们不断尝试,直到你找到一个知道……

因为动物不允许养活自己或其他动物,你没有其他选择然后创建一个类“老板”或“守门员”或“看守”,谁拥有一个私人喂养动物的方法。

你可能包括一个成员变量在动物类识别动物的年代类型然后提要功能读过基于它产生不同的结果。





相关问题
热门标签