The Non-Virtual Interface idiom in D

Denis Koroskin 2korden at gmail.com
Fri Sep 25 10:24:57 PDT 2009


On Sat, 26 Sep 2009 00:49:27 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> In this article:
>
> http://www.gotw.ca/publications/mill18.htm
>
> Herb Sutter makes a powerful argument that overridable functions  
> (customization points) should actually not be the same as the publically  
> available interface. This view rhymes with the Template Method pattern  
> as well.
>
> This leads to the interesting setup in which an interface should ideally  
> define some signatures of to-be-defined functions, but disallow client  
> code from calling them. For the clients, the same interface should  
> expose some higher-level final functions.
>
> Ignoring for the moment access protection semantics in D (which are  
> broken anyway), let's say this would work:
>
> interface Cloneable(T) if (is(T == class))
> {
>      private T doClone(); // must implement but can't call
>      T clone()            // this is what everybody can call
>      {
>          auto result = doClone();
>          assert(typeof(result) == typeof(this));
>          assert(this.equals(result));
>          return result;
>      }
> }
>
> So clients must implement doClone, but nobody can ever call it except  
> Cloneable's module. This ensures that no cloning ever gets away with  
> returning the wrong object.
>
> Pretty powerful, eh? Now, sometimes you do want to allow a derived class  
> to call the base class implementation. In that case, the interface  
> function must be protected:
>
> interface ComparableForEquality(T)
> {
>      protected bool doEquals(T);
>      final bool equals(T rhs)
>      {
>          auto result = doEquals(rhs);
>          assert(rhs.equals(cast(T) this) == result);
>          return result;
>      }
> }
>
> The difference is that now a derived class could call super.doEquals.
>
> This feature would require changing some protection rules, I think for  
> the better. What do you think?
>
>
> Andrei

Sounds pretty good. I use NVI a lot in C++, but mostly for assertion  
tests. Here is an example:

template<class T>
class InvariantChecker { /*...*/ }; calls T::checkInvariant() in ctor and  
dtor (i.e. before entering function and upon exit)

class SoundInstance
{
public:
     bool pause() {
         InvariantChecker checker(this);
         bool result = _pause();
         assert(result == isPaused());
         return result;
     }

     bool play() {
         InvariantChecker checker(this);
         bool result = _play();
         assert(result == isPlaying());
         return result;
     }

     // etc

protected:
     virtual bool _pause() = 0;
     virtual bool _play() = 0;
     virtual void checkInvariant() = 0;
};



More information about the Digitalmars-d mailing list