From: topmind on 14 Jun 2005 16:21 > > > >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. > > 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. > I agree there is a trade-off, but my observation is that change favors case statements most of the time. If you have an identical lists of options that grows in size and occurance, it probably belongs in data table. Now there are probably exceptions, and device-driver-like patterns may be one. -T- > > > ----- > Robert C. Martin (Uncle Bob) | email: unclebob(a)objectmentor.com -T-
From: topmind on 14 Jun 2005 16:33 > >> > >>>And right off the bat in this link: > >> > >>So? You were demanding examples of OO on the web that wasn't device > >>drivers; not examples of OO on the web that was likeable to you. (I > >>very much doubt the latter exists anywhere.) > > > > Huh? I was hoping to see demonstrations of polymorphism making software > > better (by whatever metric you propose) in something besides the common > > textbook domains. > > There have already been a few examples posted. Read them. Please give them names, like "The Fire Station example". > > >>(And from my experience, customers want a sysetm that _works_ and > >>doesn't give a damned about how you make it work as long as it doesn't > >>involve grand treason -- unless they can claim plausible deniability > >>in which case they don't care about the how at all.) > > > > Software is generally going to be more change-friendly if it fits the > > pattern of future changes. If the customers don't give a damned about > > your internal hierarchies, then using hierarchies is probably not a > > good change-friendly bet (unless you want to force them to live by your > > arbitrary classification system and let them vote for your competitor > > with their feet). > > That's an illogical argument. > > Do you think if a customer doesn't give a damn about the structure of a > procedural program that means the structure won't affect the future > maintainability of the software? Huh? Software that is designed to fit the patterns of actual change is most likely going to be easier to change. Thus, understanding customer psychology is going to help in planning. > > >One would just spend a lot of time reshuffling the > > tree to handle new requests and/or new combinations of requests. I want > > a bank account that is BOTH savings AND checking perhaps, and to switch > > each such feature on and aff as desired. Are you really gonna create a > > Cartesian Explosion of sub-classes for each feature combination > > (existing and new)? > > That's an illogical argument. > > The problem would only exist if you had to model everything as a single > rooted classification tree. That isn't true so your argument is meaningless. If you are going to *multiple* inheritance, you might as well use set-based feature selection. It is cleaner than MI because you can query and browse the options to see them any which way you want, not just how Bob hard-wires them into code. Code makes a crappy interface to complex data structures. > > > I assumed being change-friendly was one of the bragging points of > > polymorphism. If not, then please state your measurements of > > betterment. > > How did you measure that polymorphism is worse? I gave a link around here somewhere to some bank account illustrations where it resulted in more lines of code being changed, and/or more named units (methods/functions) being changed. Granted, it assumes that some kinds of changes are more probable than others, but barring the identification of a Tree Cop in a domain, non-tree changes seem logically more likely. Something has to shape changes into a tree if they go that way, and you have not identified that shaping force. > > Jeff Brooks -T-
From: CTips on 14 Jun 2005 17:33 Robert C. Martin wrote: > On Sun, 12 Jun 2005 07:27:44 -0400, CTips <ctips(a)bestweb.net> wrote: > > > >>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. > However, you're now left with the issue that: a) X & Y subclass shape adding X-shape & Y-shape b) X & Y don't know about each other, so they don't add an X_intersects_Y function c) intersect(X_shape, Y_shape) gets called and results in a run-time crash. IOW: in most dynamic dispatch languages, the type-system is not strong enough to ensure that at compile time [or some equivalent static analysis phase] all functions are filled in. Further, since the information about all subclasses of the type is not centralized, it is non-trivial for a user to guarantee that each of these pair-wise functions has been filled in. Contrast this to the implementation using a big-old-switch (of switches) statement. Each shape will have a tag to control the switch() statement. This tag will (probably) be an enumerated type. It is relatively straightfoward for: - the user to determine all the pairs that are required - the compiler to spit out a waring regarding an unimplemented pair
From: Isaac Gouy on 14 Jun 2005 18:55 CTips wrote: > Robert C. Martin wrote: > > On Sun, 12 Jun 2005 07:27:44 -0400, CTips <ctips(a)bestweb.net> wrote: > > > > > > > >>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. > > > > However, you're now left with the issue that: > a) X & Y subclass shape adding X-shape & Y-shape > b) X & Y don't know about each other, so they don't add an > X_intersects_Y function > c) intersect(X_shape, Y_shape) gets called and results in a run-time crash. > > IOW: in most dynamic dispatch languages, the type-system is not strong > enough to ensure that at compile time [or some equivalent static > analysis phase] all functions are filled in. In several dynamic dispatch languages the type-system is perfectly adequate: I:\test\shape\shape.nice: line 3, column 6: The implementation test failed for method nice.lang.void intersect(test.shape.Shape s1,test.shape.Shape s2): no alternative matches (test.rectangle.Rectangle, test.rectangle.Rectangle) compilation failed with 1 error > Further, since the > information about all subclasses of the type is not centralized, it is > non-trivial for a user to guarantee that each of these pair-wise > functions has been filled in. I guess we could collect those intersect methods in the same place: //--- separate file package test.intersect; import test.shape; import test.circle; import test.rectangle; intersect(Circle s1, Rectangle s2){} intersect(Rectangle s1, Circle s2){} //--- separate file package test.shape; abstract class Shape {} void intersect(Shape s1, Shape s2); //--- separate file package test.circle; import test.shape; class Circle extends Shape {} intersect(Circle s1, Circle s2){} //--- separate file package test.rectangle; import test.shape; class Rectangle extends Shape {} //intersect(Rectangle s1, Rectangle s2){} //--- separate file import test.shape; import test.rectangle; import test.circle; import test.intersect; void main(String[] args){ let r = new Rectangle(); let c = new Circle(); c.intersect(r); c.intersect(c); r.intersect(c); r.intersect(r); } > Contrast this to the implementation using a big-old-switch (of switches) > statement. Each shape will have a tag to control the switch() statement. > This tag will (probably) be an enumerated type. It is relatively > straightfoward for: > - the user to determine all the pairs that are required > - the compiler to spit out a waring regarding an unimplemented pair
From: Isaac Gouy on 14 Jun 2005 19:14
"the expression problem: How can a system be extended at the same time with new data variants and with new operations over data? Requirements: 1. Separate compilation, 2. strong static type safety, 3. no code modification." The Scala Experiment -- Can We Provide Better Language Support for Component Systems? http://lampwww.epfl.ch/~odersky/talks/asplas04.pdf |