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.