You usually have this:
struct Base
{
void preventManufacturerAboutCustomerOrder();
};
struct Fruit : public Base
{
double weight;
Colour color;
virtual float computeConsumerAttractionRate();
};
struct Apple : public Fruit
{
RebateFurnisher * rebate;
virtual float computeConsumerAttractionRate();
Apple() : rebate(0) {}
};
Let’s say you application is a shopping basket, so the drag and drop is easy to follow.
Then in your D&D item dropped code, you’ll probably write something like this:
void itemDropped(Base * base)
{
if (dynamic_cast<Fruit* >(base))
{
Fruit * theFruit = dynamic_cast<Fruit*>(base);
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping", String("Attract rate: ") << theFruit->computeConsumerAttractionRate());
}
else if (dynamic_cast<Apple*>(base))
{
Apple * apple = dynamic_cast<Apple*>(base);
float rate = apple->computeConsumerAttractionRate();
if (apple->rebate) rate *= 1/apple->rebate->getDiscountRate();
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping", String("Attract rate: ") << rate);
}
}
If you look at this code, it’ll look ok, but the second part (else if) will never we called. Why? Because Apple is a Fruit, but not the opposite, so the first branch will always succeed.
Ok, then, let’s fix the code so that the most derived class are in front:
void itemDropped(Base * base)
{
if (dynamic_cast<Apple*>(base))
{
Apple * apple = dynamic_cast<Apple*>(base);
float rate = apple->computeConsumerAttractionRate();
if (apple->rebate) rate *= 1/apple->rebate->getDiscountRate();
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping", String("Attract rate: ") << rate);
}
else if (dynamic_cast<Fruit* >(base))
{
Fruit * theFruit = dynamic_cast<Fruit*>(base);
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping", String("Attract rate: ") << theFruit->computeConsumerAttractionRate());
}
}
Ok, now it’s working.
Let’s say later on your shop get bigger, and you decide to diversify:
struct GrannySmith : public Apple
{
virtual float computeConsumerAttractionRate()
{
// Let's be more interesting than the other Apples
if (Apple::computeConsumerAttractionRate() < 0.9 && !rebate)
rebate = new SpecialDiscountRebate();
return Apple::computeConsumerAttractionRate();
}
void preventManufacturerAboutOrder()
{
sendEmail("manufacturer@bob.com", String("Great discount worked, please place an order with this rebate:")<< rebate->getDiscountRate());
}
};
First possible error:
You forgot to change the itemDropped code. The code compiles. However, you’ll never have any sale on the granny smith, since it doesn’t even appear.
Second possible error:
Someone else than you got hired to add the granny smith case, and did a search over the code. Great. He fix the itemDropped code, but, as 99% of human will do, he’ll add his code after the first else if, like this:
if (dynamic_cast<Apple*>(base))
{
Apple * apple = dynamic_cast<Apple*>(base);
float rate = apple->computeConsumerAttractionRate();
if (apple->rebate) rate *= 1/apple->rebate->getDiscountRate();
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping these delicious apples", String("Attract rate: ") << rate);
}
else if (dynamic_cast<Fruit* >(base))
{
Fruit * theFruit = dynamic_cast<Fruit*>(base);
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping these fruits", String("Attract rate: ") << theFruit->computeConsumerAttractionRate());
}
else if (dynamic_cast<GrannySmith* >(base))
{
GrannySmith * smith = dynamic_cast<GrannySmith*>(base);
float rate = smith->computeConsumerAttractionRate();
if (smith->rebate) rate *= 1/apple->smith->getDiscountRate();
AlertWindow::showMessageBox(AlertWindow::InfoIcon, "Thanks for shopping these delicious granny smith", String("Attract rate: ") << rate);
}
base->preventManufacturerAboutConsumerOrder();
}
Same error. The added code will not work.
Worst, it’ll crash too, since the preventManufacturer… method will call the GrannySmith’s version, which expected the previous code to have allocated the Rebate.