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.