Others have explained the mechanics of virtuals, inheritance and slots, but I thought I'd come back to this part or question:
Does it make sense to write a intermediate base abstract class ... with these two slots as virtual pure methods ?
I would say that that only makes sense if you have a use for that abstraction, or in other words, if you have code that operates on one or more BaseConfigurationPage
s without caring about the actual type.
Let's say your dialog code is very flexible and holds a std::vector<BaseConfigurationPage*> m_pages
. Your loading code could then look like the following. In this case, the abstract base class would make sense.
void MyWizard::loadSettings()
{
for(auto * page : m_pages)
{
page->loadSettings();
}
}
But, on the other hand, let's say that your dialog is actually pretty static and has IntroPage * m_introPage; CustomerPage * m_customerPage; ProductPage * m_productPage;
. Your loading code could then look like the following.
void MyWizard::loadSettings()
{
m_introPage->loadSettings();
m_customerPage->loadSettings();
m_productPage->loadSettings();
}
In this scenario, BaseConfigurationPage
gains you absolutely nothing. It adds complexity and adds lines of code, but adds no expressive power and doesn't guarantee correctness.
Without more context, neither option is necessarily better.
As students or new programmers we are typically taught to identify and abstract away repetition, but that's really a simplification. We should be looking for valuable abstractions. Repetition may hint at a need for abstraction or it may just be a sign that sometimes implementations have patterns. And introducing an abstraction just because a pattern is noticed is a pretty common design trap to fall into.
The design of Dolphin
and the design of Shark
look a lot alike. One might be tempted to insert a TorpedoShapedSwimmer
base class to capture those commonalities, but does that abstraction provide value or might it actually add unnecessary friction when it later comes time to implement breathe()
, 'lactate()or
growSkeleton()`?
I realise this is a long rant about a sub-question based on some simple example code, but I've recently run into this pattern several times at work: baseclasses that only capture repetition without adding value, but that get in the way of future changes.