Как разрешить производному классу вызывать методы другого производного класса? OO-дизайн - PullRequest
4 голосов
/ 09 февраля 2010

Скажи, что у меня есть что-то вроде этого - просто пример.

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

Пара вещей, Toyotas и ToyotaDriver идут вместе, то есть у меня может быть класс ToyotaFactory, который будет возвращать водителя и автомобиль. Таким образом, части являются взаимозаменяемыми и используются в разных частях кода, но Toyota и ToyotaDriver идут вместе.

Ответы [ 6 ]

3 голосов
/ 09 февраля 2010

Вы можете избежать неприглядного понижения и нарушить принцип подстановки Лискова , просто делегировав из общего интерфейса, как это:

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

Также обратите внимание, что любой объект Driver, который обнаруживает себя с объектом Toyota Car, может испытывать тот же эффект:

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

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

В этом и заключается идея: автомобили Toyata представляются водителю просто как «автомобиль», а не как «неуправляемо ускоряющийся автомобиль». Драйвер не связан с неконтролируемым интерфейсом Accelerate, то есть не знает , что должно произойти. Как только они вызовут ускорение, и если предположить, что это не вызовет сбой системы, мы можем увидеть редкое следствие принципа замены Лискова, принципа универсального отзыва.

3 голосов
/ 09 февраля 2010

Вы не можете и не должны ...

Это предназначено, чтобы защитить вас от вас:)

Любое ускорение неуправляемо может быть сделано только в Тойотах (но не в другихмоделей автомобилей), а затем определение в порядке, и вы должны сначала проверить, действительно ли автомобиль является Toyota, или все автомобиль может "ускоряться неуправляемо", а затем объявление должно быть в классе автомобилей.

Вы можете,конечно, сделайте бросок ... но спросите себя ... если вы знаете подтип, который вы получаете ... почему вы получаете автомобиль, а не Toyota ??

Править: Я до сих пор не понимаю, почему вы не можете отредактировать его, чтобы оно выглядело так:

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 голос
/ 09 февраля 2010

Я предполагаю, что Person::drive() обычно вызывает Car::accelerate() в какой-то момент.Я бы переопределил определение Car::accelerate() в Toyota::accelerate(), чтобы включить Toyota::accelerateUncontrollably().

Если Car::accelerate() не является виртуальным, и вы не можете добавить функцию virtual bool Car::isCrazy(), то нетхороший способ сделать это.Помимо юмористических аналогий, кажется, что вы пытаетесь добавить свойство к классу Car без фактического изменения класса.Просто не будет хорошего способа сделать это.

1 голос
/ 09 февраля 2010

Мне кажется, вам нужен другой тип автомобиля, он должен распространяться на тип автомобиля, который может бесконтрольно разгоняться, а потом наследовать Toyota.

По твоему замыслу, ты говоришь, что не все машины могут бесконтрольно разгоняться, а поломка - это слом твоего ОО и это нет-нет ... извините за рифму

0 голосов
/ 11 февраля 2010

Вы можете использовать шаблон 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», которая принимает тег типа и возвращает указатель. Если возвращаемое значение не равно NULL, это означает, что его можно безопасно уменьшить до типа, соответствующего этому тегу.

Производные классы переопределяют метод, проверяют их тег типа и возвращают действительный указатель, если он совпадает.

0 голосов
/ 09 февраля 2010

У меня сложилось впечатление, что использование dynamic_cast здесь абсолютно нормально. Не надо этого избегать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...