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.
In my understanding, NVI is a way to prevent/discourage adding invariants to the non-virtual base method, so the fact that you want to add invariants at this point suggests that NVI either isn't the pattern you are looking for at all, or you might want to restructure your design so that you do not need to add such invariants.
That being said an alternative to simply making your previously non-virtual interface virtual would be to employ the final keyword from C++11:
Here NVI is not implemented by the class Base, but is implemented at the level of SpecialBase since classes derived from SpecialBase can no longer override the public interface (namely foo).
In this way we are saying that the public interface of Base is allowed to be overridden (invariants may be added, or even the entire function may be reimplemented), but the public interface of SpecialBase is not.
Personally I find that this can be useful in some limited cases, but most of the time I simply wanted a more complete interface in Base in the first place.
Ultimately I think it is more common to use Base to clearly define what points of customization are allowed:
Note that there is no longer a need for the SpecialBase class layer at all.