views:

128

answers:

6

Say I have something like -- this is just an example mind you.

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

A couple things, Toyotas and ToyotaDriver go together, i.e. I can have a ToyotaFactory class which will return the driver and the car. So the pieces are interchangeable and used in different parts of the code but a Toyota and a ToyotaDriver go together.

A: 

My impression is that using a dynamic_cast is absolutely fine here. No need to avoid it.

Alexander Gessler
Cool that's what I thought.
DevDevDev
-1 Downcasting like this almost always shows a poor design decision, violates the Liskov Substitution Principle.
Dave Sims
+2  A: 

You can't and you shouldn't...

This is meant to protect you from yourself :)

Either accelerateUncontrollably can only be done in Toyotas (but not in other car models) and then the definition is ok and you should check first if the car is indeed a Toyota or the all the car can "accelerateUncontrollably" and then the declaration should be in the Car class.

You can, of course, make a cast... but ask yourself... if you do know the subtype you're getting... why are you receiving a car and not a Toyota??

Edit: I still don't see why you can't edit it to look like:

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

Now you're programming against a behavioural property, something a given object can or cannot do ... so you don't need to cast and the function at leasts makes a little more sense from a semantic point of view...

Jorge Córdoba
Yes you are correct except that I have an entire codebase of someDriver.drive(someCar). Say I want to easily switch to toyotaDriver.drive(toyotaCar). I can't just change the external interface.. the whole point is that accelerateUncontrollably is only allowed on Toyotas.
DevDevDev
Still, I think you should use the interface...
Jorge Córdoba
Because this is C++ not C# so I have to dynamic cast. Not that that is a bad thing, but you say "You can't and you shouldn't" and then you go ahead and do a dynamic cast yourself. So please...
DevDevDev
+1  A: 

Seems to me you need another type of car, it needs to extend to a type of car which can accelerate uncontrollably then have Toyota inherit from that.

By you design you are saying not all cars can accelerate uncontrollably and breaking that is break your OO and is a No-No... sorry about the rhyme.

Craig
+1  A: 

I assume that Person::drive() usually calls Car::accelerate() at some point. I would override the definition of Car::accelerate() in Toyota::accelerate() to include Toyota::accelerateUncontrollably().

If Car::accelerate() isn't virtual, and you can't add a virtual bool Car::isCrazy() function, then there's not a good way to do this. Humorous analogies aside, it appears what you're trying to do is add a property to the Car class without actually modifying the class. There's just not going to be a good OOD way of doing that.

JonM
+2  A: 

You can avoid unsightly downcasting and breaking the Liskov Substitution Principle by simple delegating from the common interface like this:

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

Now the ToyataDriver will have no idea that simply accelerating will call accelerate uncontrollably:

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

Note also that any Driver object that finds themselves with a Toyota Car object may experience the same effect:

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

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

This is the idea: Toyata Cars present themselves to a Driver as simply a "Car", not an "Uncontrollably Accerating Car." The Driver isn't coupled to the uncontrollablyAccelerate interface, i.e., has no idea what's about to happen. Once they do call accelerate, and assuming doing so doesn't cause the system to crash, we may see a rare corollary to the Liskov Substitution Principle, the Universal Recall Principle.

Dave Sims
Dude you are taking my post too literally, it wasn't about OO decomposition of Toyotas and Cars and Drivers, etc. it was a general question about well exactly my question. Thanks for the answers though.
DevDevDev
Dude, dude. Dude. OO is no laughing matter. I for one am not laughing and there is nothing whatsoever un-literal or un-humorous about my answer. Uncle Bob is watching you. Dude.
Dave Sims
So any case your question was clearly stated in the comment -- how to accelerate uncontrollably without a dynamic cast, i.e., downcasting. Downcasting is generally regarded by the OO community as bad practice and delegation is one obvious way to avoid that. I suggest if an answer is unsatisfactory because someone has taken you "too literally", then you probably have a poorly worded question.
Dave Sims
A: 

You can use the pattern COM follows:

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

The basic idea is that you define a virtual method in your base class that represents a "downcast" function that takes a type tag and returns a pointer. If the returned value is not null, the implication is that it can be safely downcasted to the type that matches that tag.

Derived classes override the method, check against their type tag, and return a valid pointer if it matches.

munificent
I'm not sure which is more frightening, the downcasting itself or the fact that you're using COM as an example of good OOP.
Dave Sims
You can eliminate the downcasting completely if you want to allow Car to know about its subclasses. Using type tags like this is a pretty common pattern in C++ generic programming. boost uses it extensively, as do lots of other libraries.I'm certainly not saying the COM *product* is good OOP, just that the `QueryInterface()` part of it is very simple and pretty useful.
munificent
Yeah munificent that's essentially what I did. Glad to see somebody understood the question...
DevDevDev
In their defense, there isn't a lot of C++ expertise here. Things get a lot easier in a language that has rich type information at runtime.
munificent