如何允许派生类在其他派生类上调用方法? OO设计

时间:2010-02-09 01:06:40

标签: c++ design-patterns oop

说我有类似的东西 - 这只是一个关注你的例子。

class Car
{
   void accelerate();
   void stop();
}

class Person
{
   void drive(Car car);
}

class Toyota : public Car
{
   void accelerateUncontrollably();
}

class ToyotaDriver : public Person
{
   void drive(Car car)
   {
      // How to accelerateUncontrollably without dynamic cast?
   }
}

有几件事,丰田汽车和ToyotaDriver会在一起,即我可以有一个ToyotaFactory课程,它将返回司机和汽车。因此,这些部件可以互换并用于代码的不同部分,但丰田和ToyotaDriver一起使用。

6 个答案:

答案 0 :(得分:3)

你不能也不应该......

这是为了保护你自己:)

加速控制只能在丰田车上完成(但不能在其他车型中完成)然后定义是可以的,你应该首先检查汽车是否真的是丰田汽车或所有汽车都可以“加速不可控制地”然后宣告应该在Car类中。

当然,你可以做一个演员......但是问问自己......如果你确实知道你得到的子类型......为什么你收到一辆车而不是丰田?

编辑:我仍然不明白为什么你不能编辑它看起来像:

interface IToyotaAccelerable
{
   void accelerateUncontrollably();
}

class Toyota : public Car : IToyotaAccelerable
{
   void accelerateUncontrollably();
}

class ToyotaDriver : public Person
{
   void drive(Car car)
   {
      // Do whatever logic you want with the car...
      // How to accelerateUncontrollably without dynamic cast?
      IToyotaAccelerable accel = car as IToyotaAccelerable
      if (car != null)
      {
         accel.accelerateUncontrollably();
      } 
   }
}

现在你正在针对一个行为属性进行编程,某个给定的对象可以或不可以...所以你不需要强制转换,而且至少从一个语义角度来看,函数更有意义 ...

答案 1 :(得分:3)

您可以通过以下简单的委托方式委托来避免不雅观downcasting并打破Liskov Substitution Principle

class Toyota : public Car
{
   void accelerateUncontrollably() // can't have abstract methods here, btw
   {
       // throttle the engine unexpectedly, lock pedal beneath mat, etc.
   }

   void accelerate() // you have to implement accelerate anyway because of Car
   {
        accelerateUncontrollably();
   }
}

现在,ToyataDriver将不知道简单地加速会无法控制地加速:

class ToyotaDriver : public Person
{
   void drive(Car car)
   {
      car.accelerate();
   }
}

另请注意,使用Toyota Car对象找到自己的任何驱动程序对象可能会遇到相同的效果:

LexusDriver driver = new LexusDriver();
driver.drive(ToyotaFactory.newPrius()); // whee!

GreenHornetDriver driver new GreenHornetDriver();
driver.drive(ToyotaFactory.newCorolla()); // wow!

这就是这样的想法:Toyata Cars将自己呈现给司机只是一辆“汽车”,而非“无法控制的汽车”。驱动程序没有耦合到不受控制的加速接口,即,不知道即将发生的事情。一旦他们确实调用加速,并假设这样做不会导致系统崩溃,我们可能会看到Liskov替换原则,即普遍召回原则的罕见推论。

答案 2 :(得分:1)

在我看来,你需要另一种类型的汽车,它需要扩展到一种可以无法控制地加速的汽车,然后让丰田从中继承。

根据你的设计,你说的并不是所有的汽车都能无法控制地加速而打破这是打破你的OO并且是一个不 - 不...抱歉韵。

答案 3 :(得分:1)

我认为Person::drive()通常会在某个时候调用Car::accelerate()。我会覆盖Car::accelerate()Toyota::accelerate()的定义,以包含Toyota::accelerateUncontrollably()

如果Car::accelerate()不是虚拟的,并且您无法添加virtual bool Car::isCrazy()功能,那么就没有好方法可以做到这一点。除了幽默的类比之外,你想要做的就是在没有实际修改类的情况下向Car类添加属性。这样做不会是一个很好的OOD方式。

答案 4 :(得分:0)

我的印象是在这里使用dynamic_cast绝对没问题。没必要避免它。

答案 5 :(得分:-1)

您可以使用以下模式:

class Car
{
   void accelerate();
   void stop();

   virtual Car* specifyModel(int modelID)
   {
       return NULL;
   }
}

class Person
{
   void drive(Car car);
}

#define MODEL_TOYOTA 1

class Toyota : public Car
{
   virtual Car* specifyModel(int modelID)
   {
      if (modelID == MODEL_TOYOTA) return this;
      return NULL;
   }

   void accelerateUncontrollably();
}

class ToyotaDriver : public Person
{
   void drive(Car car)
   {
      Toyota* toyota = static_cast<Toyota*>(car.specifyModel(MODEL_TOYOTA));

      if (toyota != NULL)
      {
         toyota->accelerateUncontrollably();
      }
   }
}

基本思想是在基类中定义一个虚方法,该方法表示一个带有类型标记并返回指针的“向下转换”函数。如果返回的值不为null,则暗示它可以安全地下载到与该标记匹配的类型。

派生类重写方法,检查其类型标记,并返回有效指针(如果匹配)。