D
Daniel T.
"Greg said:Roger said:Greg said:On the contrary, FooList demonstrates exactly
how a class should encapsulate its data - with private data members
and
a public interface. Note that clients cannot access FooList's data
member directly, instead they must invoke methods in FooList's
public
interface to access FooList's data. In other words, FooList has
encapsulated its data by mediating all access to it.
I guess I don't see the difference between op[] as instantiated here
and making array public.
There is a huge difference between using operator[] and making the
array public. With a public array, clients can bypass FooList's
interface (and FooList's methods) and obtain the data FooList stores -
directly. And without FooList's interface interposing itself between
its stored data and its clients there is no easy way for FooList to use
a different data storage mechanism in the future, since its clients
will all be relying on the data being stored in an array.
Accessing the data objects through the operator[] is a completely
different story. The operator[] is a function - it is therefore code in
FooList that clients must call in order to retrieve data from a FooList
object. Since FooList can implement operator[] however it likes, it can
get the data it returns from anywhere it likes. And since every client
must call this method to get the data, no client is relying on any
detail of how FooList actually stores its data. In this example FooList
happens to use an array data member - but it need not to. For clients,
the overloaded operator[] creates the illusion that FooList is - or has
- an array. But an interface is separate from the implementation. In
fact, with operator[] access, FooList clients have no way of knowing
how FooList actually stores its data.
Please be more clear, the above is simply not the case as I have already
shown, *unless* we document that clients of FooList only use the op[] to
access the data. In other-words, what keeps the encapsulation intact is
the documentation, not the function.
I agree that is one of the primary benefits of encapsulation, but so
is data hiding. The data is not being hidden in any robust sense here.
I don't see the difference, in my example, between op[] and making
array public. It's disheartening to me to see that one of the
much-vaunted advantages of C++ over C is so easily, so naturally, so
intuitively and quite often subverted.
It's important to ask what exactly FooList aims to encapsulate - and
the answer is not Foo objects, not by any means. Simply put, FooList
does not encapsulate the Foo objects that it stores. Containment has
nothing to do with encapsulation. Since FooList does not implement Foo,
FooList cannot encapsulate Foo. Foo is an independent class with its
own interface and its own, encapsulated implementation.
So what then does FooList encapsulate? It encapsulates just what it
implements: a storage mechanism for Foo objects. That's it. But
otherwise FooList knows almost nothing about Foo objects. The classes
are not related, so FooList is just a client (and barely one at that)
of Foo.
So does the fact that FooList returns Foo objects break its
encapsulation? Absolutely not. FooList is a container - a container is
expected to return whatever it logically contains. That's why it's
called a container after all. As we can conclude from the FooList
example, a container does not encapsulate the items that it stores -
only the way that it stores them. Encapsulation is not about data
organization or relationships between classes - it describes solely the
relationship (within a single class) between a public interface and an
implementation.
Now the above I think is a good write-up, and one that I can generally
agree with. Except as follows, the FooList's sole responsibility is to
ensure that the Foos it holds have their destructors called at the
appropriate time (as the last thing that happens to the Foo object
contained.) So we must ask ourselves, can FooList make such a guarantee
with the interface provided? Well, if some member-function returns a
pointer/reference to one of the contained Foo's, it can't. It's up to
the clients of FooList to restrain from using the free access that
FooList provides.
So yes, encapsulation is broken, but that (as I have already explained)
is not necessarily a bad thing, because FooList doesn't really own the
objects it contains anyway... Ultimately, the responsibility is on the
owner of the Foos.