代码之家  ›  专栏  ›  技术社区  ›  DevDevDev

如何允许派生类对其他派生类调用方法?面向对象设计

  •  4
  • DevDevDev  · 技术社区  · 15 年前

    比如说,我有——这只是一个很好的例子。

    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?
       }
    }
    

    有几件事,丰田章男和丰田章男在一起,也就是说,我可以 ToyotaFactory 将返回司机和汽车的类。所以这些部件是可以互换的,可以在代码的不同部分使用,但丰田和丰田河是一个整体。

    6 回复  |  直到 15 年前
        1
  •  0
  •   Alexander Gessler    15 年前

    我的印象是使用 dynamic_cast 这里很好。没必要回避。

        2
  •  3
  •   Jorge Córdoba    15 年前

    你不能也不应该…

    这是为了保护你自己:)

    要么加速不可控制只能在Toyotas中完成(但不能在其他车型中完成),然后定义是正常的,您应该首先检查汽车是否确实是丰田,或者所有的汽车都可以“加速不可控制”,然后声明应该在汽车类中。

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

    编辑: 我还是不明白为什么你不能把它编辑成:

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

    现在,您正在针对行为属性编程,某个给定对象可以或不能执行的操作…因此,您不需要强制转换,而且至少在某些情况下,函数的含义更为合理。 从语义角度看 ……

        3
  •  3
  •   Dave Sims    15 年前

    你可以避免不雅 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();
       }
    }
    

    还要注意 任何 发现自己与丰田汽车对象在一起的驾驶员对象可能会体验到相同的效果:

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

    这是一个想法:Toyata汽车把自己呈现给一个司机仅仅是一辆“汽车”,而不是一辆“无法控制的汽车”。司机并没有耦合到无法控制的加速接口上,即 不知道 接下来会发生什么。一旦他们称之为加速,假设这样做不会导致系统崩溃,我们可能会看到一个罕见的推论,里斯科夫替代原则,普遍召回原则。

        4
  •  1
  •   Craig    15 年前

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

    通过你的设计,你是说不是所有的汽车都能加速失控,打破这是打破你的OO,是一个不-不…对押韵感到抱歉。

        5
  •  1
  •   JonM    15 年前

    我认为 Person::drive() 通常呼叫 Car::accelerate() 在某个时刻。我会推翻 汽车::加速() 在里面 Toyota::accelerate() 包括 Toyota::accelerateUncontrollably() .

    如果 汽车::加速() 不是虚拟的,不能添加 virtual bool Car::isCrazy() 函数,那么就没有一个好的方法可以做到这一点。除了幽默的类比,你要做的似乎是在不修改类的情况下向汽车类添加属性。没有一个好的方法可以做到这一点。

        6
  •  -1
  •   munificent    15 年前

    您可以使用以下模式com:

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

    基本思想是在基类中定义一个虚拟方法,该方法表示一个接受类型标记并返回指针的“downcast”函数。如果返回的值不为空,则意味着可以安全地将其向下转换为与该标记匹配的类型。

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