The Non-Virtual Interface idiom in D

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Sep 26 07:06:24 PDT 2009


Michel Fortin wrote:
> On 2009-09-25 16:49:27 -0400, Andrei Alexandrescu 
> <SeeWebsiteForEmail at erdani.org> said:
> 
>> 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.doEquals(cast(T) this) == result);
>>          return result;
>>      }
>> }
> 
> [Note: I corrected the above example by replacing rhs.equals with 
> rhs.doEquals.]
> 
>> 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?
> 
> I think you're writing a lot of boilerplate code for something that the 
> compiler should be able to do by itself. I mean, it's a lot cleaner with 
> contracts, and there is no reason the compiler couldn't generate itself 
> that "contract-verifying" non-virtual function.

I think it would be a mistake to latch on my quick examples. It's not 
only about before and after checks, it's more about low-level 
customization points versus higher-level interfaces.

Andrei



More information about the Digitalmars-d mailing list