[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: OO should break when broken



Quoting Colin Putney <cputney@wiresong.ca>:
> I've snipped large parts of Vesa's post, mostly because he objects to 
> the simplifications I made in order to keep my argument short, but does 
> not explicitly disagree with my conclusion.

You have tried to argue that Ellipse should be a subclass of Circle and I
disagree with that. This should be very clear from my posts, but
understanding this does require careful thought.

> Again, I think shapes are not a good example to use in discussions of
> object modeling, because the mathematical classification of shapes that
> everyone is familiar with does not map to a class hierarchy that models
> those shapes well.

I basically disagree on this count also, because one learns a great deal
more on OO by tackling an example that is fairly well defined (a few
hundred years of exact mathematics) and requires careful thought. At the
very minimum, the classes in any OO example should have some well defined
properties that one can use to formally show that subclassing makes sense.
(Sadly, examples in many low quality introductory OO texts use classes
whose properties are barely defined if at all.)

> > Stated in another way, the essense of a subclass is to constrain the
> > set of states of the superclass.

The above is an approximate characterization of the situation. For a more
formal characterization, involving states, read my previous post. A more
accurate characterization, that is not limited to state, would say that
the essense of a subclass is to constrain the superclass. If we limit
ourselves to state, then a more precise characterization would be the one
that I started with in my previous post which said that the subclass
constrains the superclass state in respect to the additional state in the
subclass.

> Interesting. Would you care to give an example, or explain why you 
> believe this?

If you reread my previous post, you will find that it talks about the set
of states that objects of the superclass and the subclass may take (please
take some time to understand it). The discussion about the states is a big
part of the reason why I think that the above characterization is
basically accurate.

It is hard to not come with an example. In fact, I find it more difficult
to find meaningful subclasses that are not constraining their superclasses
in any way.

A simple example would be a LinkedList[WithSlowSize] class that does not
cache the size (or length or number of nodes in the list) of the list and
a subclass LinkedListWithFastSize that would cache the size of the list in
additional state. It is very easy to see that in LinkedListWithFastSize,
the set of valid states of the superclass portion (the linked list head)
and the set of valid states of the added state (the size or the number of
nodes in the linked list) are strongly dependent (specifically, for a
specific length, only lists that contain exactly that many nodes are valid
and for a specific list only a single length is valid) and that the set of
valid states is a small proper subset of the cartesian product of the
independent parts of the valid state.

On the other hand, if the added state in the subclass is independent of
the superclass portion of the state, then there must be a change in the
interface specification (an additional constraint) or the subclass would
simply not generally be useful.

> > 2. One just can not generally ignore behaviour (= functions and
> > procedures) when dealing with software. Representational (data)
> > hierarchies are useless unless they satisfy useful invariants that can
> > be exploited by behavioral entities (= functions and procedures).
> 
> Sure. But it's not terribly useful to talk about behaviour unless you 
> have an idea of the function the software is to perform. So basically, 
> the whole discussion boils down to "it depends on your application." 
> For sake of argument, assume that all the shape classes have to 
> implement a simple interface: #width, #height and #draw.

Then, for the sake of argument, I'm given the task to implement a function
that returns the diagonal vector of a tight bounding rectangle for a given
Circle. A perfectly valid implementation of the function for Circles could
only call either #width or #height (but not both) and wouldn't work
correctly when given an Ellipse.

In fact, no matter what interface you choose, as long as the interface is
wide enough to actually make it possible to distinguish between Circles
and Ellipses, then it is possible to come up with a perfectly valid
function on Circles that will not work correctly when given an Ellipse.

Regards,
  Vesa Karvonen