Suppose I have the following hierarchy using the NVI idiom :
class Base
{
public:
virtual ~Base() {}
void foo() { cout << "Base::foo" << endl; foo_impl(); }
private:
virtual void foo_impl() = 0;
};
class A : public Base
{
private:
virtual void foo_impl() { cout << "A::foo_impl" << endl; }
};
If at some point in the hierarchy I want to "add" invariants in the non virtual base method, what would be the best way to do so ?
One way would be to recurse the NVI idiom at the SpecialBase level :
class SpecialBase : public Base
{
private:
void foo_impl() { cout << "SpecialBase::foo" << endl; bar_impl(); }
virtual void bar_impl() = 0;
};
class B : public SpecialBase
{
private:
virtual void bar_impl() { cout << "B::bar_impl" << endl; }
};
But I don't really like this idea, since I don't want to add methods (with different names) for each derived bases I add to my hierarchy...
Another way is to have the following (which is not NVI) :
class Base
{
public:
virtual ~Base() {}
virtual void foo() { base_foo(); foo_impl(); }
protected:
void base_foo() { cout << "Base::foo" << endl; }
virtual void foo_impl() = 0;
};
class SpecialBase : public Base
{
public:
virtual void foo() { base_foo(); specialbase_foo(); foo_impl(); }
protected:
void specialbase_foo() { cout << "SpecialBase::foo" << endl; }
};
class B : public SpecialBase
{
private:
virtual void foo_impl() { cout << "B::foo_impl" << endl; }
};
Which in my opinion is less confusing since at any point a concrete class just has to implement the virtual method, while a derived base class can override the base (virtual) method if it chooses too.
Is there another cleaner way to achieve the same ?
EDIT:
I'm looking for a very general design pattern that could allow me to have the following kind of hierarchy :
Base <- A
<- B
<- SpecialBase <- C
<- D
<- VerySpecialBase <- E
<- StrangeBase <- F
Where each Base
class can (and will override foo), whereas classes A-F
will only need to reimplement foo_impl
.
Note that just adding another optional customization virtual function (e.g bar_impl
) won't help here, because it only allow for one extra layer of customization, where I could possibly need an infinite number.