实现基类的集合——我是否需要在派生类中进行如此多的向下转换?



我正在实现一个类的集合,它们表现出以下模式:

 public class Animal {}
 public abstract class AnimalToy
 {
      public AnimalToy(Animal owner)
      {
           Owner = owner;
      }
      public Animal Owner { get; private set; }
      /* Various methods related to all toys that use the Owner property */
 }
 public class Dog: Animal 
 {
      public void Bark() {}
 }
 public class PlasticBone: AnimalToy
 {
      public PlasticBone(Dog owner) : base(owner) {}
      public void Throw()
      {
           ((Dog)Owner).Bark();
      }
 }
  • 我有一个基类AnimalToy,其属性是对另一个基类Animal的引用。
  • 我现在想为Dog类实现DogPlasticBone玩具。PlasticBone是一个只对狗有效的玩具,实际上构造器限制PlasticBone的主人为Dog类型。
  • PlasticBone有一个惟一的Throw()方法,它使用Dog (Bark())上惟一的Dog类的方法。因此,在我可以访问它之前,我需要将通用属性Owner转换为Dog

这工作得很好,但在项目中,我正在处理这种情况一次又一次地出现,并导致相当丑陋的代码,其中派生类的方法充满了基类引用的向下转换。这正常吗?或者有没有更好的整体设计更干净?

有一种方法可以解决这个问题:

public abstract class Animal 
{
    public abstract void MakeNoise();
}

让dog实现MakeNoise,你可以在你的Toy类中调用它:

public void Throw()
{
    Owner.MakeNoise();
}

可以将AnimalToy类设置为泛型,这样可以避免强制类型转换。

public abstract class AnimalToy<TAnimal> where TAnimal : Animal
{
    public AnimalToy(TAnimal owner)
    {
        Owner = owner;
    }
    public TAnimal Owner { get; private set; }    
}
public class PlasticBone: AnimalToy<Dog>
{
    public PlasticBone(Dog owner) : base(owner) {}
    public void Throw()
    {
        Owner.Bark();
    }
}

值得注意的是,((Dog)Owner)不是上cast,它被称为下cast。

我将尝试在评论中详细说明一些讨论,并希望为您提供一个通用的多态解决方案。总体思想在功能上与Carra所建议的相同,但希望这将帮助您将这些概念应用于实际问题的领域。

对你的问题进行抽象处理;假设您的抽象基类定义如下:

public abstract class Animal
{
    public abstract void Catch();
}

这是抽象方法语义上的一个转变;您不再拥有抽象的Bark方法,您只拥有一个Catch方法,其语义将其定义为"对抛出的内容做出反应"。Animal的反应完全取决于动物。

那么,你可以这样定义Dog:

public class Dog : Animal
{
    public override void Catch()
    {
        Bark();
    }
    public void Bark()
    {
        // Bark!
    }
}

并将Throw方法更改为:

public void Throw()
{
    Owner.Catch();
}

现在,Bark是特定于Dog的,而通用的Catch方法是通用于所有Animal的。Animal类现在指定它的子类必须实现一个方法,该方法对抛出给它的项作出反应。事实上,Dog的吠叫完全取决于Dog

这就是多态性的本质——不能由玩具来决定狗做什么;这取决于狗。玩具只需要知道狗能抓住它。

更一般地说,我不喜欢两个扩展独立基类的类在逻辑上耦合(并行层次结构,正如Eric指出的那样),除非其中一个显式实例化另一个,尽管在没有看到实际的解决方案架构之前,不可能提供任何真正的建议。

最新更新