开发者

How to implement an interface class using the non-virtual interface idiom in C++?

In C++ an interface can be implemented by a class whose methods are pure virtual.

Such a class could be part of a library to describe what methods an object should implement to be able to work with other classes in the library:

class Lib::IFoo
{
    public:
        vir开发者_运维技巧tual void method() = 0;
};

:

class Lib::Bar
{
    public:
        void stuff( Lib::IFoo & );
};

Now I want to to use class Lib::Bar, so I have to implement the IFoo interface.

For my purposes I need a whole of related classes so I would like to work with a base class that guarantees common behavior using the NVI idiom:

class FooBase : public IFoo // implement interface IFoo
{
    public:
        void method(); // calls methodImpl;

    private:
        virtual void methodImpl();
};

The non-virtual interface (NVI) idiom ought to deny derived classes the possibility of overriding the common behavior implemented in FooBase::method(), but since IFoo made it virtual it seems that all derived classes have the opportunity to override the FooBase::method().

If I want to use the NVI idiom, what are my options other than the pImpl idiom already suggested (thanks space-c0wb0y).


I think you've got your NVI pattern around the wrong way: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface

Not sure if that solves your issue though.

class IFoo
{
    public:
       void method() { methodImpl(); }
    private:
       virtual void methodImpl()=0;
};

class FooBase : public IFoo // implement interface IFoo
{
    private:
        virtual void methodImpl();
};

Here's an example of why you might do this using a reader that reads from XML and another from DB. Note that common structure is moved into the NVI readFromSource, while non-common behaviour is moved into the private virtual getRawDatum. This way logging and error checking is only needed in the one function.

class IReader
{
  public:
    // NVI
    Datum readFromSource()
    {
       Datum datum = getRawDatum();
       if( ! datum.isValid() ) throw ReaderError("Unable to get valid datum");
       logger::log("Datum Read");
       return datum;
    }
  private:
    // Virtual Bits
    Datum getRawDatum()=0;
};

class DBReader : public IReader
{
  private:
    Datum getRawDatum() { ... }
};

class XmlReader : public IReader
{
   private:
     Datum getRawDatum() { ... }
};


Commonly, the reason for using the NVI (sometimes also called "Template Method") is that derived classes should only change a part of the base class' behavior. So what you do is this:

class base {
  public:
    void f()
    {
      // do something derived classes shouldn't interfere with          
      vf();
      // do something derived classes shouldn't interfere with          
      vg();
      // do something derived classes shouldn't interfere with          
      vh();
      // do something derived classes shouldn't interfere with          
    }
  private:
    virtual void vf(); // might be pure virtual, too
    virtual void vg(); // might be pure virtual, too
    virtual void vh(); // might be pure virtual, too
};

Derived classes can then plug into f() at the spots they are meant to and change aspects of f()'s behavior, without messing up its fundamental algorithm.


It may be confusing that once a method is declared as virtual in a base class, it automatically becomes virtual in all derived classes, even if the virtual keywords is not used there. So in your example, both methods of FooBase are virtual.

... to deny derived classes the possibility of overriding the common behavior implemented in FooBase::method()...

If you can get rid of IFoo, and just start the hierarchy with FooBase with a non-virtual method, that would do it. But it looks like you want to allow direct children of IFoo to override method(), but to prevent children of FooBase to override it. I don't think that's possible.


You could use the pimpl-idiom to achieve this:

class IFoo
{
    public:
        IFoo( boost::shared_ptr< IFooImpl > pImpl )
            : m_pImpl( pImpl )
        {}

        void method() { m_pImpl->method(); }
        void otherMethod() { m_pImpl->otherMethod(); }
    private:
        boost::shared_ptr< IFooImpl > m_pImpl;
};

class IFooImpl
{
    public:
        void method();
        virtual void otherMethod();
};

Now others can still subclass IFooImpl and pass it to IFoo, but they cannot override the behavior of method (they can override otherMethod). You can even make IFooImpl a direct subclass of IFoo and use enable_shared_from_this to initialize IFoo correctly. This is just the gist of the method. There are many ways to tweak this approach. For instance you can use the factory-pattern to make sure IFoos are created correctly.

Hope that helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜