[Exceptional C++ Style] Fw: const data - was Item 17

Balog Pal pasa at lib.hu
Mon Dec 20 09:45:56 EST 2004


>> The class in question represents a grammar. It contains one (const) data
>> member for every non-terminal contained in the grammar. A grammar
>> typically contains several hundred non-terminals. The file itself is
>> generated by another program. The whole grammar is constant and contains
>> no behaviour in and of itself. It is simply a large and reasonably
>> intricately structured bit of constant data.
>
> So, Jon's point was NOT about public data in general.

And I'm very glad to see this. :)  As a plenty of reasoning in this item is
a CHEAT somewhere. At least can be used that way if we close our eyes and
write some holy words like 'no public data' on our flag.   Instead of
memorizing the bold guideline of this itemabout getting the INTERFACE right.

Right interface can't be summoned on general terms, and spoke about without
a well-established context.   Th shape and amount of both public data and
functions must be considered with the task, purpose of the stuff we are
creating.

In general, we have multiple strong forces fighting each other.
One is encapsulation -- thoroughly analysed in item text. That would force
us to maximal hiding, minimise coupling and dependencies.
The other forces are coming from XP experience -- to keep things the
simplest, and solve the problems we have instead of going to premature
generalisations or solving the problems possible for the next century.
In real life we should find the right balance between those, otherwise we're
wasting resources.

Public data certainly hits a large hole on the capsule -- we practically
forfeight any control on that data item.  Is that bad?  Not in itself. It is
bad if we need the control. it is suboptimal, if we may need the cotrol
later.  If is not bad if control is irrelevant.

If i see a piece of public data somewhere my first question is 'Will you be
happy if anyone just wrote some arbitrary new value there?'
Looking at a trivial/empty accessor function I could similarly ask, 'Is
there any possibility to have any other body of that function?'
A 'No' answer then means the interface is fouled up.

Example 17-b in the item is supposedly a case of good, encapsulated data.
Too bad it really isn't.  And showing it that way, can be well misleading.
The problem with logic is subtle: we have 2 candidates, show some problems
with one, and jump to conclusion the other is good. Without first looking
after the alternatives cover the whole universe.

Fowler's excellent book 'Refactoring' shows many examples of the most basic
refatorings. With discussion on how we get into the situation, and how it
can be solved by moving responsibilituies around, split, join, redistribute.
Having public data practically prevents all those activities, while if we
have functions instead, we can get away just modifying those functions.

The catch is in the details: we can get away depending on how the function
is prototyped, and what the change is. If all we have is

T1& UseT1();

that pretty much limits what we can do.  Also it breaks other guidelines
about 'do not leak handles to your internal parts'.  My own experience too 
showed none/extremely little utility of this form, so since I classify it as 
'phony encapsulation' and avoid it -- having no better idea I consider even 
leaving the data public is better, at least that stands out. :)

That form has two basic prolems, one it mixes read and write access, and two 
it returns reference.  For the former the general solution is to have 
distinct getter and setter function, the first a const one.  The 
ref-returning is a more interesting issue.  After separating read and write, 
we're not obliget to return the identity of the underlying object, so a 
serious desing decision shall be made if the fetched thing shall be the 
value or the identity.    And if vlaue is needed whether some optimisation 
issues stil force to return a ref to some existing stored element.

I think this is the point we arrived to the real stuff to see: we're 
arriving from "encapsulate the data", the wrong end, we shall be arriving 
from the other if we're designing a class: to know the behavior!  If we do 
it good, all we have is the set of functions, and some data (if any) will 
get produced as implementation detail.  The whole question is just 
sidestepped. ;-)

If the getter genuinely returns a value, we're no longer tied to some 
representation, also the data member can dissipate entirely, replaced by 
runtime production of the result.

But placing all that back in the 17-2 reasoning we'll see it's no longer a 
free lunch.

- ------------

The pair/cuple example is very good, just is IMHO points different 
conclusions.
That class has absolulely no knowledge on what its members are, and needs no 
control.  Wrapping access to first and second serve no point at all.   It is 
a template class -- will someone put the extra stuff, 'meat' of accessor 
function in the main template?  Or will we write specialisation?

For the 'deleted' thing it is absolutely a matter of design. We can't tell a 
thing without knowing the semantics of 'deleted'.  If read and/or write to a 
deleted object is considered forbidden, certainly leaving the data public is 
wrong, and accessor functions shall enforce the policy.  If OTOH it is 'just 
there'...   Also if we change the *meaning* later, it is delision to expect 
everything will be alright, and one shall not look up the uses even if the 
compiler can compile the code without complaints.







More information about the Effective-cpp mailing list