>Actually cohesion is a measure of how singular in purpose an object >is, which I suppose may be what you mean when you say "how well the >methods in an object stick together." Getters and setters don't >necessarily make an object more cohesive, but having methods in the >object that are external to the core purpose of that object (which is >where I am predicting I have a fundamental philosophical difference >with what you're saying here) would make it less so. >
Just to clear this up, i'm not promoting the use of huge objects with all kinds of methods stuffed into them. In my mind, calling address.render() and addressRenderer.render(address) is equivalent (and in fact the second approach is much better since it gives you two smaller, more cohesive, objects with a nice separation of concerns). Let's go into even more boring detail: if address.render() calls addressRenderer.render(this) internally... you'd never know from the outside! My point is that either of these is better than having non address related display code somewhere outputting (and optionally formatting) a bunch of address.getX() calls (actually, once you do that, that code _is_ address related... happy find and replace when you need to change it!). >> Secondly, an object shouldn't have any idea about the outside world, > >True, but the corollary isn't necessarily true, meaning the outside >world might need to know *something* about the object. At a minimum >the outside world (meaning the system at large) needs to know the >object exists and how to use it, otherwise the object is of extremely >limited use to the system. > The outside world needs to know the interface - the public methods - of the object in order to use it. >> but an object _does_ care about how it is used: this is why >objects have >> both public and private methods, this is where the whole >concept of an >> object's interface comes from, this is one of the >fundamental tenets of >> object orientation: an object interface embodies a contract that says >> "if you use me in these very specific ways (list of methods and >> arguments) i will work for you." > >But isn't that precisely what getters and setters are? Providing a >public interface to the object's data seems to me to be nothing more >than a list of methods and related arguments that allow other objects >in the system to use the object in question. > I'd say they allow the other objects in the system to use the object's *data*. An object is about behavior and data, not just data. >> <tr> >> <td>#Product.getName()#</td> >> <td><cfif Product.isPromo()>Special! >> </cfif>#Dollarformat(Product.getPrice())#</td> >> </tr> >> >> i'm sorry, but that the exact same code you would have written with a >> query - the fact that you sprinkle on a few parenthesis and use >> sexyCamelCasing to name your "columns" doesn't magically make it OO. > >True in that case, but if this were a query you also have less >functionality than if this is a bean with getters AND setters. The >setters are what you're missing with a query object. For output of >multiple records I still use query objects in most cases. When >dealing with single records using a bean-like object is a very >flexible and powerful way to go. > Getters and setters are almost synonymous with bean-like objects, and are part and parcel of how bean like objects are supposed to be used. My point is about the proliferation of getters and setters all over your code, including in business objects which should not need them. >> When you implement getters and setters for >> your private instance fields, you are exposing the values stored in >> these fields to use and manipulation OUTSIDE the objects in >which they >> live. > >But if the data is *only* private and no one can get to it, what use >is it to the rest of the system? OO systems are all about building >flexible objects that can communicate with one another through >publicly exposed methods. The control of an object over its own data >comes in the implementation of these publicly exposed methods, not >through whether or not something outside the object itself can >manipulate the data. If an outside process such as someone filling >out a form can't manipulate the data inside an object, I'm not sure >what the point of having the object is in the first place. > You can ask an object to do something with that data and tell you how it turned out: Assuming an account object with a private field balance, which is better: - <cfif account.getBalance() - requestedWithdrawalAmount gte 0>/* process the withdrawal */</cfif> or - <cfif account.authoriseWithdrawal(requestedWithdrawalAmount)>/* process the withdrawal */</cfif> As for loading data from a form - this is a clear cut example of where you would want accessors. You would probably use some kind of form bean, load the fields therein from the form scope using setters, and then pass that bean to a business object (although getters/setters aren't the only way to do that). This whole diatribe of mine isn't about never writing another getter or setter in your life: it's about only writing getters and setters where you really need them... which is to say probably writing 90% less than you do today. The whole "use public accessors to get or set private data" is _the_ OO 101 example, page one, paragraph one - but most people seem to read that as "provide public accessors for ALL private data fields" which is wrong because you might not need to access those fields. >> Imagine you have an address object made up of a number of fields - >> street, state, country, etc. If you go down the >getter/setter road, you >> are basically inviting code outside your address object to >know too much >> about what constitutes an address - and this "expertise" constitutes >> tight coupling. > >I completely disagree with the notion that other objects in the system >having to "know" they have to call getStreet() to get the street >address is tight coupling. getStreet() is nothing more than a public >method that renders the object usable to the system at large. Without >a public interface the objects are essentially useful only to >themselves, which greatly reduces their power. > Firstly, why does an outside object have to know about the components of an address? Why is hiding how you do something so important, but hiding what something is made of less important? Secondly, do you agree that the acid test of coupling is how many objects do you have to modify to effect any given change in a system? Because if so, overuse of getX() is going to lead to having to change a lot of places when Street changes... Now let's put this in context: imagine you need to make a change to the street variable: - nirvana: you only have to change one object. In practice, this will almost never happen (unless your object is called God.cfc or you are making a truly trivial change) - horrorshow: search through all your code... cfm, cfc, inc, whatever, and look for getStreet() and setStreet() calls, then figure out what you were doing with the value of street there, and how that may have to change now... many cigarettes, much coffee.. and you will miss some. - best you can hope: you make your changes in a small, well known number of places, and you go to bed confident that you have got them all. Why do i claim that getX() calls will lead you down path b? Because it's the dark side of the force: quicker, easier, more seductive. Just reach right into that object and grab a little piece of its data..., after all, what's the threat? It's all protected by accessors, right? How can it possibly hurt? >> Whether there is a private string field called "street" >> or whether the string return value of getStreet() is >computed at runtime >> is irrelevant - why do i need to know that an address is >composed of a >> street in the first place? > >Because something else in the system might need to pull the value of >the street address from the object for some reason? It sounds like >you're making the assumption that the only thing the object will ever >need to do is display itself. How does the system actually *use* the >object if the object doesn't allow the rest of the system to >communicate with it? > If something else in the system really needs to know the value of the street, then you implement the getStreet() method (although i would fight long and hard to define "really needs to know"). My argument is about what comes first: the getter or the need for a getter? In many cases today, people are blindly implementing the getter without a true need, in a kind of "OO checklist mentality", and then abusing the getter to treat an object as little more than a data structure. >> I have no idea whatsoever how an internal >> combustion engine works or what parts it is composed of: i just know >> that you push _this_ pedal to make it go and _this_ pedal to make it >> stop. > >I don't see a distinction between pushing the gas pedal in your car >and calling getStreet() on an address object. You have to know where >the pedal is and that you have to push it down to go somewhere. If >you don't know that minimal information, it's not as if the car is >going to drive itself. The gas pedal is the publicly exposed method >for telling the car to go. Without that you aren't going to get very >far, but you don't need to know a single thing about *how* the car >implements the go() method to drive. > The difference is between car.go() and starter = car.getStarter(); starter.start(); fuelpump = car.getFuelPump(); fuelpump.pump(); ... many lines omitted to disguise the fact that i have *no* idea how a car works... >> If you add a >> field to address and wind up having to change the order display view, >> that should set off some warning bells in your mind. > >What sets off warning bells in my mind is having an address object >know how it's going to be displayed. Your domain model objects >shouldn't know or care how they're going to be displayed. > So put that code in a renderer object! You would never consider sprinkling an object's CRUD code all over your application, but <cfoutput><b>#address.getStreet()#</b></cfoutput> is fine? And before you tell me that that exact code would likely appear in my renderer object, let me preempt you by saying that i know, that you will probably be passing in some kind of bean made up of little else than getters and setters to the renderer (although... memento!, but that's another day), and that (icing on the cake) - that's the ONLY place it would appear. ---------------------------------------------------------- You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email. CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com). An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
