Re: colored point redux
Vadim Nasardinov <[email protected]> Sat, 24 Feb 2007 22:23:35 -0500
| Newsgroups | gmane.comp.lang.lightweight |
|---|---|
| Message-ID | <[email protected]> |
On Saturday 24 February 2007 16:16, Pascal Costanza wrote:
> (defgeneric eqv (obj1 obj2)
> (:method-combination and)
> (:method and (obj1 obj2)
> (eql (class-of obj1) (class-of obj2))))
[...]
> Better?
Much better because it's more honest. We have now completely
abandoned any pretense of Liskov substitutability: 'COLOR-POINT is no
longer a subtype of 'COLOR. Which is as it should be. If we are
completely honest, however, we should take this a step further and
change the IS-A relationship to HAS-A.
As Eric Gamma says, "Favor composition over inheritance":
http://www.artima.com/lejava/articles/designprinciples4.html
| Bill Venners: The other principle of object-oriented design that
| you offer in the GoF introduction is, "Favor object composition
| over class inheritance." What does that mean, and why is it a
| good thing to do?
|
| Erich Gamma: I still think it's true even after ten
| years. Inheritance is a cool way to change behavior. But we
| know that it's brittle, because the subclass can easily make
| assumptions about the context in which a method it overrides is
| getting called. There's a tight coupling between the base class
| and the subclass, because of the implicit context in which the
| subclass code I plug in will be called. Composition has a nicer
| property. The coupling is reduced by just having some smaller
| things you plug into something bigger, and the bigger object
| just calls the smaller object back. From an API point of view
| defining that a method can be overridden is a stronger
| commitment than defining that a method can be called.
Which brings us to:
| public final class Point {
| private final int x;
| private final int y;
|
| public Point(int x, int y) {
| this.x = x;
| this.y = y;
| }
|
| public int getX() { return x; }
| public int getY() { return y; }
|
| public boolean equals(Object obj) {
| if (!(obj instanceof Point)) { return false; }
| Point that = (Point) obj;
| return this.x == that.x && this.y == that.y;
| }
| }
|
| public final class ColorPoint {
| public enum Color {
| COLORLESS, GRUE;
| }
|
| private final Color color;
| private final Point point;
|
| public ColorPoint(Color color, int x, int y) {
| if (color == null) { throw new NullPointerException("color"); }
| this.color = color;
| this.point = new Point(x, y);
| }
|
| public Color getColor() { return color; }
| public Point getPoint() { return point; }
|
| public boolean equals(Object obj) {
| if (!(obj instanceof ColorPoint)) { return false; }
|
| ColorPoint that = (ColorPoint) obj;
| return this.color.equals(that.color) && this.point.equals(that.point);
| }
| }
No schmultiple dispatch needed.
Why don't we talk about something really important and interesting
instead? Say, loops vs. tail-recursive iteration.
Olin "Sig Sauer" Shivers has this totally awesome and
under-appreciated paper called "The Anatomy of a Loop":
http://scholar.google.com/scholar?q=Olin+Shivers+anatomy+of+a+loop
The video is much more entertaining (and almost as informative):
http://video.google.com/videoplay?docid=-3704713569771882785&hl=en
In it, he discusses, inter alia, what's wrong with Scheme's perceived
insistence that loops be implemented as tail-recursive functions. He
also points out a few issues with the Common Lisp loop "facility".
Good stuff.