On 8/25/06, Daniel Hulme <[EMAIL PROTECTED]> wrote:
That's because you're used to one way of thinking about class inheritance: that the subclass can do everything that the superclass can do, and more. In this scheme, you might have a Square class, with a field representing its corner and another giving its side length. Then, you could build on this to have Rectangle as a subclass, which adds an extra side length, and extra accessors for it. This is a really bad way of making your subclasses work, but your Rectangle has all the fields and methods of your Square, and some extra ones.
This is the well-known Circle/Ellipse problem, and it relates to the theory of value types. We'll first take the traditional conception that a Square is a Rectangle, the opposite of your viewpoint. This is based on the geometrical definitions of these shapes. This makes sense from a usage standpoint: sub rect_perimiter(Rectangle $x) { 2*$x.width + 2*$x.height; } sub square_perimiter(Square $x) { 4*$x.width; } It makes sense to pass a Square to rect_perimiter, but not to pass a Rectangle to square_perimiter. That is one indication that Square is a subtype of Rectangle. However, say that Rectangle had set_height() and set_width() methods. You cannot set the height and width of a square independently, so a function like this: sub make_dims(Rectangle $r, $w, $h) { $r.set_width($w); $r.set_height($h); POST { $r.area == $w * $h } } Must fail if $w and $h are not equal (and depending on Square's implementation, might fail if they are equal). You could say that the failure is on the implementation side: we couldn't implement set_width and set_height appropriately. So clearly a Square is not a Rectangle. Let's look at the other way around (your viewpoint): a Rectangle is a Square. This makes sense from an implementation point of view, as you point out. We just take Square's methods and add a couple of capabilites. However: sub area(Square $x) { $x.width ** 2; } Fails (by returning the wrong thing, worse than dying) if you pass it a Rectangle. So it failed from the usage point of view. You could say "that should be a method", but then you should say that everything that uses a square should be a method of square, because we must be able to make assumptions about the behavior of classes. If you say that my sub makes an assumption that it shouldn't, realize that the only thing it is assuming is that a Square is a geometrical square. If I can't make that assumption, then Square is not a very good name for that class. So clearly a Rectangle is not a Square. One way failed on the implementation side, the other on the usage side. I'd argue that the "Rectangle is a Square" view's validity can only be argued from an implementation laziness point of view (which is okay, but it must also be balanced with other issues). The "Square is a Rectangle" point of view only works if they are value types: if you are not allowed to modify anything. In that circumstance, it works very cleanly[1]. But if you are allowed to modify things, then the two classes must be siblings, not parent-child related. Luke