From: Robert C. Martin on 14 Jun 2005 15:07 On 11 Jun 2005 20:45:47 -0700, "topmind" <topmind(a)technologist.com> wrote: > >> >> >> And bound to the RDBMS. >> > >> >You OO'ers always make that sound like a bad thing. Youses are >> >DB-phobics. I don't want to be bound to OO, can we wrap that away too? >> >(I agree that the DB tools are sometimes lacking in implementation and >> >portability). >> > >> >> If we had done that, our tests would be too >> >> slow, and we would have to ship the system to our users with some kind >> >> of RDBMS attached. >> > >> >My RDB-bound wiki was not "slow". >> >> You misunderstand. I run over 1,000 unit tests, and over 100 >> acceptance tests on the system. These tests combined take about 90 >> seconds to run (on a bad day). The reason for this speed is that I >> use the in-memory version of the page object. > >I am not sure what your point is. My point was that I was not suggesting that your RDB-bound wiki was slow. I *was* suggesting that my tests would run slower if they used a database. >Do you have tests that show >a RDBMS implementation would not scale as well? No, it didn't seem necessary. I think I could finish running all the unit tests before the RDB had finished building a connection. (slight exaggeration). >> > >> >Anyhow, one can do something like this: >> > >> >function getWikiArticle(articleID) { >> > if (sys::driver==RDBMS) {.....} >> > elseif (sys::driver==Files) {.....} >> > elseif (sys::driver==RAM) {.....} >> > else {error()) >> >} // end-function >> >> True, one could do that. However, that makes a generic function >> (getWikiArticle) depend upon three different implementations. That >> kind of coupling is unfortunate. I'd rather have the getWikiArticle >> function not know about RAM, RDBMS, and FILES, and that's what >> polymorphism gives me. > >If we had hundreds of different "drivers" I could see the advantage of >polymorphism. However, that is not likely in this case. Companies don't >want to pay for multiple implementations of the same thing in most >cases and RDBMS are a safe bet for most apps in my domain. It would be >a poor investment to target the 1% who may want to use flat files >because they hate Dr. Codd. Nobody is talking about hating RDBs. In any case the cost of the polymorphism was negligible, and it quickly enabled our user to add the RDB plug-in when he needed it. Without that polymorphism, his task would have been quite a bit more difficult. >And, I am not fully clear on what you mean by "depend upon". Given two binary modules A and B. If A mentions B, or a unique name that lives within B, then A depends on B. This means A cannot be deployed without B. It also means that if B changes, there is a strong likelihood that A will need to be recompiled and redeployed. Our user who produced the RDB plug-in for FitNesse was able to create a new binary module that FitNesse did not depend upon. He then modified a config file that told FitNesse the name of that binary module. FitNesse used that binary module instead of it's internal modules for page persistence. Your point above about polymorphism being useful if there were hundreds of different drivers comes into play here. We don't know how many drivers there are for FitNesse. There *may* be hundreds of them. The use of polymorphism has enabled our users to create them, without forcing them to modify the source code of FitNesse to do so. Consider, as another example, an ATM machine. The basic unit can do standard things like deposit, withdrawal, and transfer. However, the manufacturer would like to sell new features to their users, such as pay gas bill, pay electric bill, pay credit card, etc. If the software developers use polymorphism to dispatch to the features, then the manufacture can sell new features simply by selling new DLLs or jar files, without having to alter the source code of the existing system, nor change the binary code of the ATM machines already in use. >> >But I would really like to see poly in business-modeling issues, not >> >storage and EXE distribution issues. >> >> Then look at the way FitNesse handles everything else. Or look at the >> payroll example in the PPP book. (The library likely has a copy. If >> not, a local Barnes and Noble will have one.) > >What are the hierarchies used? Please don't tell me it uses "account >types". I addressed that in a sister message. The word "Hierarchy" is a bit misleading. Although there is a lot of polymorphism used in the example, there are very few class hierarchies that have more than two levels. Typically the top level is an interface, and the next level down completely implements that interface. There are some exceptions to this rule, especially in some of the transaction types. Basically the structure looks like this: |Payroll|----->|Employee| * | +--------->|PayClass| | +--------->|PaySchedule| | +--------->|PayDelivery| Each of the classes on the right is an interface, for which there are several derivatives (shown below). The algorithm in the Payroll class is (partially): void Payroll::pay() { foreach Employee e { if (e.isPayDay(today)) { Money pay = e.calculatePay(); e.deliverPay(pay); } } } The Employee class looks something like this: class Employee { private PayClass payClass; private PaySchedule paySchedule; private PayDelivery payDelivery; public bool isPayDay(Date date) { return paySchedule.isPayDay(date); } public Money calculatePay() { return payClassification.calculatePay(); } public void deliverPay(Money pay) { payDelivery.deliverPay(pay); } } There are three derivative of PayClass: |PayClass| A | +----------------+------------------+ | | | |HourlyPayClass| |SalariedPayClass| |CommissionedPayClass| | | |* |* V V |TimeCard| |SalesReciept| The 'calculatePay' method of PayClass is polymorphic. It is implemented in HourlyPayClass to sum up the contained time cards and calculate overtime. It is implemented in SalariedPayClass to return the monthly salary. It is implemented in CommissionedPayClass to sum up the sales reciepts, apply a commission rate, and add a base salary. There are three derivatives of PaySchedule: WeeklySchedule, MonthySchedule, BiWeeklySchedule. The 'isPayDay' method of PaySchedule is polymorphic. It is implemented in WeeklySchedule to return true if the argument is a Friday. Monthly schedule returns true if the argument is the last business day of the month. BiWeekly schedule returns true only for every other Friday. There are three derivatives of PayDelivery: MailDelivery, DirectDepositDelivery, and PaymasterDelivery The 'deliverPay' method of MailDelivery causes the employees paycheck to be mailed to him. You can guess what the others do. This is just one very small part of the payroll application as described in the www.objectmentor.com/PPP book. One nice thing about this design is that the code inside the payroll class can be placed in a binary module (a dll or jar file) and deployed independently from the rest of the system. If we have a customer who has nothing but hourly employees, we can simply ship the payroll module, and module that contains HourlyPayClass. Moreover, users can add new PayClasses, Schedules, and Delivery mechanisms without having to alter the source code of the payroll application. >> It's good for many things, not for everything. It's use is >> situational, not specific to particular domains. There are situations >> within every domain in which polymorphism is both useful and not. >> That includes drivers, persistence, telecommunications, business >> rules, guis, etc, etc. Wherever dependencies need to be managed >> (which is just about everywhere) there is a potential for polymorphism >> to facilitate that management. > >Well, perhaps if the situations where it is useful and not are >clearified, we might find our areas of agreement. Is it only helfpul if >one can find a good, safe hierarchical classification or multiple >implementations of the same thing (device drivers)? I described this in a different email. In summary, polymorphism is useful when you want to be able to add new data structures (like PayClass derivatives, PaySchedule derivatives, or PayDelivery derivatives) without affecting existing functions (like Payroll:pay). Switch statements are useful when you want to add new functions without affecting existing data structures. ----- 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: Isaac Gouy on 14 Jun 2005 15:25 Robert C. Martin wrote: > 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. For example, Nice, http://nice.sourceforge.net/ abstract class Shape {} class Circle extends Shape {} class Rectangle extends Shape {} void intersect(Shape s1, Shape s2); intersect(Circle s1, Circle s2){} intersect(Circle s1, Rectangle s2){} intersect(Rectangle s1, Circle s2){} intersect(Rectangle s1, Rectangle s2){} void main(String[] args){ let r = new Rectangle(); let c = new Circle(); c.intersect(r); c.intersect(c); r.intersect(c); r.intersect(r); } > 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). For more information on binary methods in OO, multimethods and double-dispatch, see On Binary Methods (1995) Kim Bruce, Luca Cardelli, Giuseppe Castagna, et al. http://citeseer.ist.psu.edu/3113.html
From: Isaac Gouy on 14 Jun 2005 15:48 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. > > 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 Seems like what's become known as "the expression problem" See "Independently Extensible Solutions to the Expression Problem" Matthias Zenger, Martin Odersky (200KB) http://scala.epfl.ch/docu/files/IC_TECH_REPORT_200433.pdf
From: Isaac Gouy on 14 Jun 2005 15:48 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. > > 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 Seems like what's become known as "the expression problem" See "Independently Extensible Solutions to the Expression Problem" Matthias Zenger, Martin Odersky (200KB) http://scala.epfl.ch/docu/files/IC_TECH_REPORT_200433.pdf
From: topmind on 14 Jun 2005 16:16
> >>Says the person who hasn't presented objective evidence > >>supporting his position. > > > > It is my opinion that paradigm preference is largely a *subjective* > > thing. Thus, there is not much to objectively prove either way. > > Then why do you keep asking for objective evidence from the OO guys? > If you guys also believe the benefits of OO or poly are subjective instead of absolute, then simply say so and then we are done. That is not the message I get from youses. -T- |