From: Phlip on 14 Jun 2005 13:19 Robert C. Martin wrote: > However, with OO, adding new functions to existing data structures is > hard. You have to find every subclass and add a new method. But you can do that incrementally, which a switch can't so easily do. You can add the new method as a default in the base class, test, deploy one specialization to one subclass, test, and keep going until nobody uses the default. Then you make it pure virtual and retire its stub implementation. -- Phlip http://www.c2.com/cgi/wiki?ZeekLand
From: Robert C. Martin on 14 Jun 2005 13:31 On Sun, 12 Jun 2005 09:14:15 +0200, "Ilja Preuý" <it(a)iljapreuss.de> wrote: >Robert C. Martin wrote: > >> The tougher one is: >> >> Shape intersection(Shape s); >> >> That requires a generic shape that can represent any shape at all. > >Well, it depends on the other operations on the Shape class, I think. (I >might just not understand what you mean by a "generic shape", though.) Let's say we have a simple shape hierarchy of polygons and ellipse, including corresponding arcs. The intersection of the interiors of two polygons is not necessarily a single polygon. The intersections of the interiors of two ellipses is generally not an ellipse. And the intersection of an ellipse and a polygon is some horrible hybrid. An object that could represent those intersections must be much more generic than Polygon or ellipse. How do we represent these intersections as shapes? Topmind suggested that intersections be represented *as* intersections. i.e. we simply represent the resultant shape by storing the two intersecting shapes in a new derivative of Shape, perhaps called ShapeIntersection. (What remains, thereafter, is figuring out how to draw, calculate perimeter, calculate area, rotate, etc, etc, a ShapeIntersection.) ----- Robert C. Martin (Uncle Bob) | email: unclebob(a)objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 "The aim of science is not to open the door to infinite wisdom, but to set a limit to infinite error." -- Bertolt Brecht, Life of Galileo
From: Robert C. Martin on 14 Jun 2005 13:41 On Sun, 12 Jun 2005 07:27:44 -0400, CTips <ctips(a)bestweb.net> wrote: >Robert C. Martin wrote: >> On Fri, 10 Jun 2005 18:57:21 -0400, CTips <ctips(a)bestweb.net> wrote: >> >> >>>topmind wrote: >>><snip> >>> >>>>OO is good for .... shapes. >>>> >>> >>>Not really. Anytime someone comes up with the shapes example, ask them >>>how they would add the method: >>> >>>class shape { >>>// true if object has any points in common with <B> >>>boolean intersects(shape B); >>>} >>> >>>See how quickly mind-lock sets in.... >> >> >> That one is not so hard. Simply algebra can solve it without too much >> difficulty (I've done it). The tougher one is: > >Nah, the problem isn't the implementation. Its the fact that you have to >(in general) have to write a separate function for each _pair_ of >subclass types. You *may* have to do that, if you don't have a generic way of representing shapes. Let's say that we don't. The solution to this problem has been around for a long time. It's called multiple dispatch. (e.g. some languages allow you to polymorphically dispatch on more than one argument. Thus (intersect(s1, s2) selects a function that conforms to the type of both s1 and s2. Most OO languages don't have multiple dispatch. However, just as general trees are isomorphic with binary trees, so too is multiple dispatch isomorphic with dual dispatch. (i.e. the Visitor pattern). Visitor provides a very nice solution to *dispatching* aspect of the shape intersection problem. Indeed, using Visitor for this kind of stuff is pretty much run-of-the-mill OO. The harder problem to solve is how to accurately represent the resultant intersected shapes. Now *that* is a problem worth study. >This means that you need to know _ALL_ other shape sub-classes out there >before you can actually implement the function intersects(). Granted. And when you add new shapes you have to modify the intersection visitor. That's just part and parcel of the problem. >[I have deliberately picked some fractal shapes so that a certain >mathematical ignoramus (not you) doesn't start quibbling; however I am >_NOT_ certain that intersects() is actually computable for both the koch >snowflake and the mandelbrot set.] I don't know about the snowflake. As for the Mandelbrot set, it is not solvable in general, but is trivial to solve to any particular tolerance. Remember that a point is in M only if the recursive iteration *eventually* converges. Practically speaking, one must choose a tolerance for *eventually*. Otherwise one could spend an infinite amount of time checking certain points. ----- Robert C. Martin (Uncle Bob) | email: unclebob(a)objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 "The aim of science is not to open the door to infinite wisdom, but to set a limit to infinite error." -- Bertolt Brecht, Life of Galileo
From: Robert C. Martin on 14 Jun 2005 13:45 On Tue, 14 Jun 2005 04:35:23 GMT, Jeff Brooks <jeff_brooks(a)nospam.com> wrote: >Jeff Brooks wrote: > >> It appears you don't you understand what a fractal is. > >Oops, that should say "It appears you don't understand what a fractal is." A fractal is like a newsgroup thread. They are all self similar regardless of scale. ----- Robert C. Martin (Uncle Bob) | email: unclebob(a)objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 "The aim of science is not to open the door to infinite wisdom, but to set a limit to infinite error." -- Bertolt Brecht, Life of Galileo
From: Miguel Oliveira e Silva on 14 Jun 2005 14:01
"Robert C. Martin" wrote: > On 12 Jun 2005 10:11:39 -0700, "topmind" <topmind(a)technologist.com> > wrote: > > > > >Switch/case statements are more flexible. > > For some thing, and not for others. > > If you use switch statements then adding functions to existing data > structures is relatively easy. All you do is replicate the switch > statement in the new function, and fill in all the cases. > > However, with switch statements, adding new data structures to > existing functions is hard. You have to find every existing switch > statement and add the new case. > > If you use polymorphism then the reverse is true. Adding new data > structures to existing functions is easy. All you do is write the new > class, and all the existing functions polymorphically deploy to it. > > However, with OO, adding new functions to existing data structures is > hard. You have to find every subclass and add a new method. This is not (often) true. In languages in which sub-typing (substitutability) is sub-classing (code reuse), it is only necessary to implement the new method, in the classes with sufficient information for that implementation. As an example, suppose we have a class TWO_FIGURE_INTERACTION exporting a "distance" real method, with many descendant classes (such as CIRCLE_RECTANGLE_INTERACTION). (Eiffel code:) class TWO_FIGURE_INTERACTION feature distance: REAL is -- minimum distance between the two figures deferred -- method without implementation ("pure" virtual in C++ terminology) end; ... end -- TWO_FIGURE_INTERACTION If this class's ADT (Abstract Data Type) is to be extended with a new method "intersect", then it is only necessary to add it in this class (regardless of their descendant classes): class TWO_FIGURE_INTERACTION feature intersect: BOOLEAN is do Result := distance < 0.0 end; ... end -- TWO_FIGURE_INTERACTION Since ADT are much (much) more stable than methods, adding new operations to existing ADTs (at least as ADTs are implemented in OO languages!), has lesser effects in the program's modularity than adding new data types to existing methods. The reason for this very important difference is in the key continuity modularity criteria (a small change in the problem's specification results in changes in one or few modules). Although both approaches (OO and functional/procedural) are in a sense dual (as you correctly mentioned), the duality is not balanced at all. An ADT need not to know all of its methods possible implementations (it may even not know one!). On the other hand, the "switch" alike method approach needs to know most (if not all) of the possible data types. > Sometimes you want to be able to add new functions to existing data > structures, and so you use a switch statement. Sometimes you want to > be able to add new data structures to existing functions, and so you > use polymorphism. > > They are yin and yang, opposite and complementary. Using just one or > the other is imbalanced. > > ----- > Robert C. Martin (Uncle Bob) | email: unclebob(a)objectmentor.com > Object Mentor Inc. | blog: www.butunclebob.com > The Agile Transition Experts | web: www.objectmentor.com > 800-338-6716 > > "The aim of science is not to open the door to infinite wisdom, > but to set a limit to infinite error." > -- Bertolt Brecht, Life of Galileo -miguel -- Miguel Oliveira e Silva mos at det.ua.pt - http://www.ieeta.pt/~mos DET-IEETA, Universidade de Aveiro, PORTUGAL |