这个设计使用动态还好吗?

时间:2014-02-18 02:56:55

标签: c# design-patterns dynamic polymorphism dispatch

我已经在我的应用程序中列出了要处理的不同作业。我正在使用一种使用不同类型来表示不同类型作业的设计 - 这很自然,因为它们具有不同的属性等等。对于处理,我正在考虑根据下面的代码在C#中使用dynamic关键字。

abstract class Animal {}    
class Cat : Animal {} 
class Dog : Animal {}

class AnimalProcessor
{
    public void Process(Cat cat)
    {
        System.Diagnostics.Debug.WriteLine("Do Cat thing");
    }

    public void Process(Dog dog)
    {
        System.Diagnostics.Debug.WriteLine("Do Dog thing");
    }

    public void Process(Animal animal)
    {
        throw new NotSupportedException(String.Format("'{0}' is type '{1}' which isn't supported.",
            animal,
            animal.GetType()));
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        List<Animal> animals = new List<Animal>
        {
            new Cat(),
            new Cat(),
            new Dog(),
            new Cat()
        };

        AnimalProcessor animalProcessor = new AnimalProcessor();
        foreach (dynamic animal in animals)
        {
            animalProcessor.Process(animal);
        }

        //Do stuff all Animals need.
    }
}

代码按预期工作,但是,我有一种唠叨的感觉,我错过了一些非常明显的东西,并且有一个更好(或更好的已知)模式来执行此操作。

有没有更好或同样好但更被接受的模式用于处理我的动物?或者,这样好吗?并且,请解释为什么任何替代方案都更好。

3 个答案:

答案 0 :(得分:8)

你没有遗漏任何东西:调度对象的运行时类型是dynamic可以做得很好的事情。

您确实有其他方法,例如实施visitor pattern,但在实施方面要高得多,并且它的可读性也不如使用dynamic的方法那样可读:

interface IAnimalProcessor { // The visitor interface
    void Process(Cat cat);
    void Process(Dog dog);
    void Process(Animal animal);
}
class AnimalProcessor : IAnimalProcessor {
    ...
}
interface IProcessable {
    void Accept(IAnimalProcessor proc);
}
class Cat : IProcessable {
    public void Accept(IAnimalProcessor proc) {
        proc.Process(this); // Calls the Cat overload
    }
}
class Dog : IProcessable {
    public void Accept(IAnimalProcessor proc) {
        proc.Process(this); // Calls the Dog overload
    }
}
...
AnimalProcessor animalProcessor = new AnimalProcessor();
foreach (IProcessable animal in animals) {
    animal.Accept(animalProcessor); // The animal will call back the right method of the proc
}

答案 1 :(得分:3)

这绝对是糟糕的设计 你根本无法为AnimalProcessor引入接口, 因为添加新的Animal子类会导致界面发生变化 更重要的是,AnimalProcessor的客户端应该知道类使用的特殊构造(foreach with dynamic)。 如果您仍想在这种情况下使用“动态”,请考虑以下内容:

class AnimalProcessor
{
    public void Process(Animal animal)
    {
        dynamic concreteAnimal = animal;

        DoProcess(concreteAnimal);
    }

    private void DoProcess(Animal animal)
    {
        throw new NotImplementedException();
    }

    private void DoProcess(Cat cat)
    {
        System.Diagnostics.Debug.WriteLine("Do Cat thing");
    }

    private void DoProcess(Dog dog)
    {
        System.Diagnostics.Debug.WriteLine("Do Dog thing");
    }
}

至少,您将来可以提取界面并拥有不同的AnimalProcessor实现。 而且你仍然会遇到一个严重的问题:这绝对不是很明显,在添加子类之后你需要将方法添加到某个独立的类中,否则你的系统就会抛出。

答案 2 :(得分:1)

您可能想要做的是不使用动态类型并使用您的基本类型。 这会表现得更好。

在您的情况下,因为您正在调用基本流程方法。这是基本的Polymorphism

foreach (Animal animal in animals)
{
    animal.Process();
}