0

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.

  • *"since I don't want to add methods (with different names) for each derived bases I add to my hierarchy"* I'm not sure this is such a bad idea. After all, you want to *add invariants*, i.e. the derived classes also need to maintain those new invariants. – dyp Feb 14 '14 at 19:21
  • Considering your edit, I almost wonder if the Decorator Pattern would give you the layers of customization that you are looking for. – YoungJohn Feb 14 '14 at 21:42

2 Answers2

0

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:

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() final // note the use of 'final'
        { 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; }
};

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:

class Base
{
    public:
        virtual ~Base() {}
        virtual void foo() { base_foo(); bar_impl(); foo_impl(); }

    protected:
        void base_foo() { cout << "Base::foo" << endl; }
        virtual void bar_impl() {} // bar_impl is an optional point of customization
                                   // by default it does nothing

        virtual void foo_impl() = 0; // foo_impl is not optional derived classes
                                     // must implement foo_impl or else they will be abstract
};

class B : public Base
{
    private:
        virtual void bar_impl() { cout << "SpecialBase::foo" << endl; }
        virtual void foo_impl() { cout << "B::foo_impl" << endl; }
};

Note that there is no longer a need for the SpecialBase class layer at all.

YoungJohn
  • 946
  • 12
  • 18
  • I understand your point, and most probably NVI is not what I'm looking for. However, I'm looking for a more general idiom than just having an optional point of customization ... The SpecialBase is needed in my hierarchy and does not only override the implementation of foo(). There could also be base classes deriving from SpecialBase that would again reimplement foo() .. So I'm really looking for a more general design pattern. I edited my question to add the real workflow I'm looking for. – user1766172 Feb 14 '14 at 20:40
0

This post was suggested to me as similar to something I was browsing related to NVI the other day, hence the necro.

I would suggest adding a Check-Adding mechanism in the base class, so that derived classes can add requirements. This works in a very straightforward way as long as the requirements can be tested using the base class access functions, otherwise your special MyInvariant class has to dynamic_cast the base argument of doCheckInvariantOK() for the invariant to work.

edit: I understand 'invariant' to be along the lines of pre- and post-conditions of foo(), as in formal verfication. If you want to add functionality before and/or after base_foo(), what I think you're actually after, you can do it in an analogous fashion.

class Base
{
public:
    virtual ~Base() {}
    void foo() 
    { 
        cout << "Base::foo" << endl;

        //Can use invariants as pre and/or postconditions for foo_impl
        for(const std::unique_ptr<InvariantBase>& pInvariant : m_invariants)
        {
            //TODO cout << "Checking precondition " << pInvariant->GetDescription() << endl;
            if(!pInvariant->CheckInvariantOK(*this))
            {
                //Error handling
            }
        }
        foo_impl(); 
    }

protected:
    void AddInvariant(std::unique_ptr<InvariantBase>&& pInvariant)
    {
       m_invariants.push_back(std::move(pInvariant));
    }
    struct InvariantBase
    {
        bool CheckInvariantOK(const Base& base)
        {
            return doCheckInvariantOK(base);
        }
        private:
            virtual bool doCheckInvariantOK(const Base& base) = 0;
    };

private:
    std::list<std::unique_ptr<InvariantBase>> m_invariants;
    virtual void foo_impl() = 0;
};

class A : public Base
{
private:
    virtual void foo_impl() { cout << "A::foo_impl" << endl; }
};

class SpecialBase : public Base
{
public:
     SpecialBase() 
        : Base()
     {
        AddInvariant(std::unique_ptr<MyInvariant>(new MyInvariant() ) );
     }
private:
    void foo_impl() { cout << "SpecialBase::foo" << endl; bar_impl(); }
    virtual void bar_impl() = 0;

    struct MyInvariant : public InvariantBase
    {
        virtual bool doCheckInvariantOK(const Base& base) override
        {
            //TODO: special invariant code
        }
    };

};

class B : public SpecialBase
{
private:
    virtual void bar_impl() { cout << "B::bar_impl" << endl; }
};
Daniel
  • 1,407
  • 1
  • 12
  • 22
  • Interesting. I'd personally opt for a `std::vector>`: you'll then be able to register invariants as lambda expressions, which can capture their context from the derived constructor, hence removing the need for downcasting. – Quentin Jul 24 '18 at 16:03
  • @Quentin: Could you elaborate on that? How would the lambda expression know its construction context in doCheckInvariantOK()? – Daniel Jul 24 '18 at 16:06
  • I mean ending up with `AddInvariant([] { return /* check */; });` inside `SpecialBase::SpecialBase`. The lambda can capture `this` with the correct static type, then type erasure is done via `std::function` instead of OO polymorphism. – Quentin Jul 24 '18 at 16:09