[Exceptional C++ Style] Item 18 - Virtuality

Kevlin Henney kevlin at curbralan.com
Tue Jan 4 09:17:21 EST 2005


In message <41DA9608.2040404 at jaggersoft.com>, Jon Jagger 
<jon at jaggersoft.com> writes
>
>>C++ standard library already follows this guideline.
>>The standard library has:
>>6 public virtual functions, and 142 nonpublic virtual
>>functions.
>>
>>The author mentions one WinFX (the object-oriented
>>successor to the Win32 API and the programming model
>>for Longhorn) design guideline which recommends using
>>‘Template Method’ pattern and discusses why this
>>pattern is such a good idea.
>>
>Playing the devils advocate much of the design of the C++ library and 
>Win32 API leaves one feeling, how shall I put it, somewhat "empty"

A very good way of putting it. With the exception of the STL, there is 
much in the standard library that leaves the reader empty but the 
library rather full :->

One must always be aware of playing with numbers: anyone familiar with 
statistics will recognise that the numbers in question do not support 
any specific hypothesis. When it comes to class hierarchy design the 
standard library is filled with many counterexamples, which makes good 
design that much harder to teach to people :-( Such simple numbers 
should not be thrown about lightly and without qualification.

>>Traditionally, programmers write base classes using
>>public virtual functions which simultaneously specify
>>both the interface and the customizable behaviour.
>>
>Surely it is a base class that contains any implementation that is 
>"guilty" of specifying both the interface and some behaviour (whether 
>it be customizable or not)?

Indeed. If the problem is that programmers provide virtual functions 
that both specify public contract and a specific behaviour, then the 
solution is to adopt a practice where virtual functions specify the 
public contract and not a specific behaviour. This much has been known 
for some time, and is a practice supported more directly in the 
language, ie pure virtual functions, and one that does not get you into 
the problems of overcomplicating classes and introducing too many middle 
players for their own sake.

When I teach class hierarchy design, one of the considerations I 
advocate is that pure virtual functions should be overridden and 
non-pure virtual functions should not be. In other words, provide new 
implementations but don't replace existing implementations. This has 
some very useful consequences in terms of design and design questions, 
and tends to lead to more clearly structured class hierarchies, with 
clearer responsibilities and looser coupling than most other approaches. 
What you end up with is a hierarchy that is purely abstract at its roots 
and fully concrete at its leaves, with the middle taking the slack and 
strain of implementation refactoring. There are many ways of justifying 
such an architectural approach, but the view that you should not 
disinherit code is perhaps the simplest in terms of generating such an 
architecture.

The role of the middle layer is to factor out commonality in the 
concrete layer, and it is here that use of Template Method or, often 
better, a delegation-based alternative such as Strategy, Interceptor, 
Object Adapter, etc, can come into play. It is in this layer that NVI 
could be seen to have a possible role. However, unless an application of 
NVI actually factors out some commonality, it may be better to Remove 
Middle Man (http://www.refactoring.com/catalog/removeMiddleMan.html).

>>Every public virtual function is forced to serve two
>>audiences, the outside callers of the class and
>>derived classes.
>But if the base class contains only pure virtual functions it serves 
>the purpose of representing the point where the outside and the inside 
>touch. And it does not get weighed down with supposed commonality. What 
>is wrong with having a clear separation of concerns and putting 
>commonality into an ABC (containing some implementation) below the 
>interface (containing only pure virtual functions).  Pushing 
>implementation up into the base class is what causes the base class to 
>start serving two masters.

That point about serving two masters is one that can be taken further. 
To say that a virtual function must serve two audiences and use this as 
a criticism is to miss much of the point of virtual functions: the 
purpose of a virtual function is serve as a bridge between the caller of 
the function and the implementation in the derived class. That is not a 
problem that needs another level of indirection to solve, it is the 
whole point!

There are already two sides to a virtual function: its signature (the 
promise) and the definition (the fulfilment). It makes sense, reasoning 
in terms of cohesion alone, to separate these two sides, which gives 
rise to the recommendation of having pure virtual functions in an 
interface class and overriding virtual functions in derived concrete 
classes. Adding a further level of separation smothers and hides this 
important semantic and syntactic connection, weakening the intent of the 
interface and diluting its contract. It is reasonable to partition and 
subcontract work to derived classes (commonplace Template Method), but 
to always subcontract all of the work in one package does not 
automatically make sound business or software sense. It makes sense to 
cut out the fat and work directly with the abstractions, rather than 
further abstract the abstractions.

Kevlin
-- 
____________________________________________________________

   Kevlin Henney                   phone:  +44 117 942 2990
   mailto:kevlin at curbralan.com     mobile: +44 7801 073 508
   http://www.curbralan.com        fax:    +44 870 052 2289
   Curbralan: Consultancy + Training + Development + Review
____________________________________________________________




More information about the Effective-cpp mailing list