Re: Rectangle: struct or class?

From: Jerry Coffin (jcoffin_at_taeus.com)
Date: 08/19/04


Date: 18 Aug 2004 15:02:04 -0700


"Steven T. Hatton" <susudata@setidava.kushan.aa> wrote in message news:<Fv-dnVXiJIR8Or_cRVn-gw@speakeasy.net>...

[ ... ]

> I don't follow the reasoning here. Suppose I have a class called Shape which
> holds data relevant to all shapes in my program, e.g., color, position,
> velocity, display state, etc. It would make sense to derive Rectangle from
> that, if I am actually talking about a displayed graphical rectangle. It
> would also make sense to derive Circle from the same base class. But this
> derivation would fail this substitution test as well.

Unless you define your Shape class very oddly, this design would pass
the substitution test perfectly -- in fact, it's pretty much what I
suggested.

> I've never heard of
> the Liskov Substitution Principle, but google turned this up:

I'd do some more looking, and read about it in more detail. For most
practical purposes, it's the single principle that guides all use of
derivation.
 
> http://c2.com/cgi/wiki?LiskovSubstitutionPrinciple
>
> "What is wanted here is something like the following substitution property:
> If for each object o1 of type S there is an object o2 of type T such that
> for all programs P defined in terms of T, the behavior of P is unchanged
> when o1 is substituted for o2 then S is a subtype of T."
>
> I don't believe the argument you presented follows from that principle.

Whoever wrote that definition seems to have specialized in
obfuscation, but from a practical viewpoint, following the LSP means
that you can substitute a derived object for a base object under any
possible circumstances.

> If S is Rectangle, and T is Circle, an instance o1 of Rectangle is not
> interchangeable with an o2 that is an instance of Circle. Circle is not a
> subtype of rectangle. But I didn't intend it to be. There /are/ graphics
> programs that do derive all shapes from rectangle. In such cases, the
> Rectangle is a bounding rectangle of the given shape.

There are many examples of truly horrible designs in the world. That
doesn't make it a good idea to add another.
 
> I find the LSP to be a formal statement of the obvious.

In that case you're advocating designs that obviously wrong.
 
> Sure there are. I can translate a rectangle r1 by doing r1 += p1.

IMO, you seem to be progressing from bad to worse. Adding a point to a
polygon should do exactly that: add a point to the polygon, normally
resulting in a polygon with one more vertex.

> The degenerate case of geometric object is point.

Degenerate cases rarely mean much, at least when you're talking about
derivation. It's true (for example) that a rectangle with sides of
length 0 is really a single point, an example proving your point above
valid. That means that a rectangle CAN be a point (as can a circle,
etc.) To be meaningful from a viewpoint of derivation, however, you
have to show that EVERY rectangle is a point, every circle is a point,
etc.

That's not the case, and a hierarchy that assumes it is will cause
almost nothing but problems.

Keep in mind that in the end there are two basic ideas here: we want
the compiler to allow the things that make sense, but disallow (short
of things like casting to force it to allow them) things that don't
really make sense.

The question, then, is whether you want to be able to pass a
rectangle, circle, etc., to every function, every place, that a point
is accepted. Regardless of what you think that's what the LSP states,
that IS what the compiler allows: if I have code like this:

class base {};
class derived : public base {};

void func(const base &b) {
}

then I can pass an instance of derived to func without any casting,
etc. I.e. the compiler assumes that when I use derivation, that I mean
the derived object can be substituted for the base class object.

[ ... ]

> I could even derive a Circle from a Rectangle by imposing the restriciton
> that the sides be invariantly equal, and defining the center as the bottom
> right point.

You _could_ do almost anything -- but what you're advocating here will
lead to NOTHING but problems.

> Stroustrup argues that an elipse should not be derived from a circle because
> an elipse is defined by two foci, and a circle just happens to be the
> degenerate case where the foci cooincide. Well that is not the only way to
> describe an elipse. You /can/ draw an elipse by using a radial vector
> placed at the origin. So I can also derive an Elipse fromm my Rectangle,
> and might even derive a Circle from the Elipse.

Deriving circle from ellipse is almost always a bad idea. Public
derivation means that the derived class has the full interface of the
base class. In the case of an ellipse base class, you're typically
going to have some way of setting the major and minor axes to
different values. If circle derives from ellipse, it's promising to do
the same -- but doing so would give an ellipse instead of a circle.

The bottom line: from a viewpoint of substitutability, a circle is not
an ellipse and an ellipse is not a circle. Any attempt at deriving one
from the other will normally result in problems.

[ ... ]

> In §23.4.7 of TC++PL(SE) Stroustrup tells us "Donald Knuth observed that
> 'premature optimization is the root of all evil.' Some people have learned
> that lesson all too well and condider all concern for efficiency evil. On
> the contrary, efficiency must be kept in mind throughout the design and
> implementation effort."

Good for him. In this case, he's mostly wrong. The fact is that most
people who try to take efficiency into account early in the design end
up causing themselves problems. First of all, they're usually wrong
about what to optimize, so even at best their attempt does no good.
Second, in their zeal for optimizing the wrong things, they exacerbate
the real problem. Finally, they typically make the interface too low
of level, so it's almost impossible to hide the complex parts
internally, so the real performance problems become difficult to fix.

> In the case of complex simulations, it's a good idea to be sure you don't
> put inheritnly inefficient code at the foundation.

Regardless of the type of program, there's no point in introducing
needless inefficiency. Nonetheless, if you start with a good
algorithm, express it cleanly, and provide a clean interface, chances
are roughly 99% that your code will not only be fast enough, but that
it'll be faster than most of the other code out that that was
"optimized" much more carefully.

[ ... ]

> For example, Stroustup tells us that traversing a linked list
> using a for loop is much more efficient than using recursion.
>
> There are many instances in which I like to use recursion. That knowledge
> is certain to have an impact on my future design decisions.

My advice would be simpler: use recursion when it improves readability
and understandability, but not otherwise. Certainly, use iteration to
walk a linked list, but do it because it's more understandable. If
you're coding a tree traversal, it's rarely worthwhile to do it
iteratively -- and when it is, it's NOT a question of recursion vs.
iteration per se. It's (for example) when you're talking a directory
tree, and recursion gives you a depth-first traversal which is
generally substantially slower than a width-first traversal, which
happens to be easier to to iteratively.

-- 
    Later,
    Jerry.
The universe is a figment of its own imagination.


Relevant Pages

  • Re: Rectangle: struct or class?
    ... if I am actually talking about a displayed graphical rectangle. ... > would also make sense to derive Circle from the same base class. ... If circle derives from ellipse, ...
    (comp.lang.cpp)
  • Re: Rectangle: struct or class?
    ... >> Then I ask if Rectangle should derive from Point. ... would also make sense to derive Circle from the same base class. ... derivation would fail this substitution test as well. ... neither one derives from the other, ...
    (comp.lang.cpp)
  • Re: Writing a logic for shape coordinates
    ... and slots (rectangle with a semi-circle) in the drawing and where ever ... I want to check if the hole is in the range of the sensor, ... ' Check if Little circle is completly outside or completly inside ... If LittleY - LittleR < RectY Then ...
    (microsoft.public.excel.programming)
  • Re: Writing a logic for shape coordinates
    ... Second semicircle can be on any side of the rectangle. ... corners or a rectangle to see if a rectangle is outside of a larger circle. ... I want to check if the hole is in the range of the sensor, ... If LittleY - LittleR < RectY Then ...
    (microsoft.public.excel.programming)
  • Re: Writing a logic for shape coordinates
    ... rectangle and then top left corner of the rectangle. ... The two sets conditions are written for each angle if the sensor is ... circle and rectangle, then execute the conditions that I have given ... R coordinates of each hole and some necessary info for the slot hole. ...
    (microsoft.public.excel.programming)