Skip to main content
Ben Nadel at BFusion / BFLEX 2009 (Bloomington, Indiana) with: Peter Farrell and Matt Woodward and Kurt Wiersma
Ben Nadel at BFusion / BFLEX 2009 (Bloomington, Indiana) with: Peter Farrell Matt Woodward Kurt Wiersma

OOP Getters() And Setters() - A New Programmer's Frustration

By
Published in Comments (50)

Every now and then, an article pops up that slams the use of Getter() and Setter() methods in object oriented programming (OOP); promptly, those new to OOP, like myself, shiver, wet ourselves, and franticly run around trying to escape the daunting feeling that we have no idea what in the heck we are doing. To me, these articles are never constructive - all they do is tell us that we're wrong using examples that rarely relate to our own experience (at least for me) and offer very few, if any, solutions to our common problems. You might as well tell me that I'm fat and that it's all my fault and then not offer me any dietary advice - it's basically the same thing.

As someone who is very new to OOP and struggling to wrap my head around all the new principles and best practices, I wanted to voice my frustration with such articles not so that people would stop writing them - very much to the contrary; I want to voice my frustration in hope that people will continue to write these articles and to write them much better.

So, here are my frustrations:

First off, let's not use Java-based examples. Obviously, the author can write in whatever language he or she knows. But, if you are a ColdFusion developer, don't just point me to a Java article. While ColdFusion sits on top of Java, these are very different languages and live by very different rules. Yes, I understand that the principles of object oriented programming can be ported from language to language, but remember that people like me are new to this, so help us out by providing a comfortable, familiar environment for learning.

In ColdFusion, there's really no difference between Ints, Doubles, Floats, and Strings (etc.), so simple-data-type encapsulation means nothing to me. Don't talk to me about what happens when a hundred references to an INT suddenly need to be a 100 references to a BIGINT. In a loosely typed language like ColdFusion, this is simply not a concern. Now, if a reference to an INT suddenly needs to be a reference to a Collection of objects, then yeah, we have a problem. Give a solution to gracefully hide that type of change and I can eagerly follow along.

Do people really use Builders in ColdFusion? Do they make sense in a loosely typed language? I have seen several Java examples that use Builders to allow Objects to create a sort of lightweight transfer object for their internal data which is then used to build View output. The explanation of this seems to be that the UI logic is stored in one place and that all the data in the object can be transformed to string data (internally to the object) such that its data type implementation is hidden. But my question is, if simple-data-type issues do not exist, does this sort of solution make sense? Also, is the UI logic really only in once place, or do I have to build a new Builder for every View that uses this sort of data (think single FORM view, single display view, bulk list view, bulk edit view, single delete view, bulk delete view, JSON response, XML response, etc.)? My gut is screaming at the idea of a Builder. Something about it infuriates me - now not only do I have a View, I also have to have a Model object and a Builder to help the two to communicate, not to mention a Controller; one page takes how many objects to work?

Are we going to be creating GOD objects? If we are to avoid passing around data, then doesn't that force our business objects to become huge objects? Knowing how to Validate, Save, Read, Delete, and whatever else needs to be done with the internal object data?

What about the idea of consistent access, even when done internally to an object? For example, I see objects all the time that have many calls to getDSN() rather than referring to the DSN value directly so that access logic to that variable is not duplicated. Are we supposed to get rid of all internal Get() methods as well and duplicate that type logic throughout the object?

So anyway, I am not trying to say that Getters() and Setters() are good or bad - I, quite frankly, don't understand OOP thoroughly enough to have an opinion. All I am asking for is that the articles that get written on the topic have better, more relevant examples.

Reader Comments

1 Comments

OOP is one of those classic examples where if you ask 3 people the same question, you will get 4 opinions. As somebody that has been around object oriented development long before ColdFusion ever started thinking it, there are always a dozen ways to do something and everybody has an opinion as to why their way is the best.

I personally see getters and setters as a good think in that they help you to adhere to basic encapsulation principles. However, with ColdFusion, data typing is really not necessary as you pointed out and thus I have no issue with dynamic getter and setter methods such as those that exist in the IBO concept. This way you maintain encapsulation while saving the overhead ... but, many will disagree and that is their right.

OOP can get very frustrating, especially for somebody that is coming from a purely procedural background. In some respects I am lucky that I started with OO languages before experiencing ColdFusion (boy was ColdFusion a backwards way of thinking to me!). Either way, keep at it, it does start to click after a while.

15,848 Comments

@Jeff,

I can certainly understand that there will be different implementation details among the three different people asked; however, when it comes to something as seemingly "core" as the concept of getter and setter methods, I have to believe that there is a right way and a wrong way. It like asking, "Should one breathe?" Yeah, one can breathe through the nose and another can breathe through the mouth, but we can all agree that breathing itself is essential.

Of course, that is just a gut, uneducated feeling; I may be way off.

3 Comments

Hi,

I'm not a ColdFusion developer but I see what you mean when it gets to using OOP methods and practices in a language that doesn't really support real OOP.

AS3 is one of those languages.
Design patterns in AS3 can't be useful a bit. Yet, you can find books and articles about the subject.

That's my opinion.

Regards.

38 Comments

I agree that we as ColdFusion programmers are lacking lots of good OO examples in ColdFusion. Equally by not using duck typing to make our CF code more Java like, then we are actually loosing a benefit of using CF. Also CF is quite slow a creating CFCs comparing to Java so some techniques from the Java world don't work that well in CF (For example holding a array of 5,000 employee objects in a company object).

To me getters and setters and a means to an end. They are bad only because of the way they are used. A lot of people new to OO tend to just have a get and set method for every instance variable in a class, which misses the point that objects should be aware of themselves and not just data stores.

Hopefully posts written by recent OO adopters like yours, Jason Dean http://www.12robots.com/index.cfm/ColdBox and (shameless plug) my attempts http://www.aliaspooryorik.com/blog/index.cfm/category/oop-12 will help other newbies.

Bob Silverberg has also posted some good stuff at a higher level which is fairly easy to follow http://www.silverwareconsulting.com/index.cfm/OO-Design

153 Comments

I've recently (since starting my current job 3 years ago) taken to the following mantra: "Code for right now, not for next year." Really, it boils down to YAGNI: ya ain't gonna need it.

IMHO, accessor methods increase complexity, so you should only use them when you have a concrete case for them, not just because it said you should in a book somewhere.

If you code accessor methods on the off chance that you might some day want to do some magic when a property is set, you're almost invariably hurting yourself in the long run. Unless you are coding an app that you know is going to be reused a dozen times, so that any code changes will need to be made a dozen times, keep the code simple.

If I was feeling particularly ornery, I'd argue that getters have almost no place in CF code. In an environment where effectively nothing is private, why do you need a getter? How is it harder for the API to be "read from obj.DSN, write to obj.setDSN()" rather than "read from obj.getDSN(), write to obj.setDSN()"? You're already trusting the consuming code to not muck around too much anyway. And there, you've just hacked off reams of useless code.

"But if I make a change it will break the API!" So? You're already making a change. Code that consumes your API will almost certainly have to change, anyway.

I'd also argue that in most code that I've seen, getDSN() creates the false illusion of rubustness. Coders seem to think that if they use #Obj.DSN# then "omg, what happens if I change the DSN?". Things will break. But how does using #Obj.getDSN()# change that? In a multi-threaded environment, the second is just as dangerous as the first!

Sorry, that was a bit of a rant.

(And yes, there are the special cases where your job is all about developing code that absolutely must be OO because it is being extended and reused on a daily basis. But is that what we're talking about here? How many web apps are built like that? Hint: the answer is very, very few. Unless you are building resellable frameworks, your web app is probably standalone.)

122 Comments

Part of the problem here is that it's very hard for (most) procedural programmers to shift their thinking to OO.

Getters and setters are a basic aspect of encapsulation but they don't make something an Object. They are the result of a data-focused approach to a problem rather than a behavior-focused approach. OO really requires a behavior-focused approach.

Yes, if you need to get data out of an object - or push new data into an object - then you should use getters and setters rather than raw public data members. That's encapsulation.

But the real issue is that it is "better" OO to ask an object to do something (with its own data) than to ask the object for its data and perform an operation on that data outside the object.

Exposing data (via getters and setters) should be a "last resort" if it is too hard / too complex to embed the desired behavior inside the object. A good example of this is the boundary between business objects and the database (even if you use an ORM) because you need to get raw data in/out at that point. However, when manipulating objects in general, the following (anti-)pattern in your code should ring alarm bells:

data = obj.getData();

doSomething(data);

obj.setData(data);

Consider whether the following would be better / easier:

obj.doSomething();

Yes, it can make for "big" objects but it is better encapsulation - the data doesn't even leave the premises - and it is more in keeping with the goals of OO.

Hope that helps.

122 Comments

And just to follow up that Rick O makes a good point: many web applications are *not* intended to be paragons of OO virtue - they simply don't need to be. That said, I still would not expose bare data members - I would use getters - but I probably would not use an object for bare bones configuration data. That's overkill. I tend to have a ColdSpring MapFactoryBean that contains my configuration data - as a simple stuct. There's not really anything to encapsulate in such data so making it an object is (fairly) pointless.

8 Comments

<blockquote>In ColdFusion, there's really no difference between Ints, Doubles, Floats, and Strings (etc.), so simple-data-type encapsulation means nothing to me. Don't talk to me about what happens when a hundred references to an INT suddenly need to be a 100 references to a BIGINT. In a loosely typed language like ColdFusion, this is simply not a concern.</blockquote>
....Except when you're pushing a static value into a Query of a Query

www.bennadel.com/index.cfm?dax=blog:363.view

:-)

21 Comments

@Ronny:

"using OOP methods and practices in a language that doesn't really support real OOP."

How do you define "real" OOP? The way Java does it? If you are suggesting CF isn't OO I would disagree, but I would admit that do to the structure of the language (dynamically typed) and some drawbacks of the CFML Engine (slow object instantiation) that OO design patterns in CF doesn't always look the same as other languages. I don't think that makes CF's OO "fake"

@Ben: Something interesting I found while learning ActionScript this week via Flex was that AS 3 allows for implicit getters and setters, but that doesn't mean what you probably think it means. You still write the getter and setter methods in your class, but you call them as though they aren't a method so your code looks more natural.
Take this getter for example found in the core AS Date class (note the "get" before the function name).

public function get fullYear():Number { return getFullYear(); }

You don't have to do:
var thisYear = myDate.fullYear();

You can just do:
var thisYear = myDate.fullYear;

Now frankly, I'm not sure what I think of that. I don't think it saves me any more typing, but it does make the access to the fullYear value look more natural.

140 Comments

@Sean,

Thanks for the clarity of your example of thinking OO in terms of behavior, *not* data:

"obj.doSomething();

"Yes, it can make for "big" objects but it is better encapsulation - the data doesn't even leave the premises - and it is more in keeping with the goals of OO."

Really, in your example, though the object may get 'big', it is smaller than the alternative, right? You showed 3 methods (getData, doSomething, setData) in the alternative, and only one method in the suggestion (doSomething). Not only potentially smaller objects, but it seems to me that it really captures the behavior approach: let the API simply describe what's going to be done, let the object worry about how it accomplishes the same. On the premises as you put it.

9 Comments

I think the post and conversation itself devolved a bit into the concept of "big" objects. IMO, objects shouldn't be measured by size. A "big" object is perfectly appropriate if the object has a lot of properties and/or behaviors. Big Object != God Object (necessarily). As long as the object is doing what it needs to do and only what _it_ needs to do, then the size is irrelevant.

Encapsulation is a vastly more important consideration than size. Allow the object to grow to whatever size it needs, but don't break encapsulation.

I'm sure that Sean et al didn't intend to imply otherwise, but if the post is geared to those new to OOP, I thought it might be worth mentioning.

78 Comments

Just to throw a brief example in here, I think in many cases an internal getter can be even more important than an external one. It has to do with a little advertised bit of advice called the "law of demeter" or the "principal of least knowledge".

http://en.wikipedia.org/wiki/Law_Of_Demeter

The name is good because it gives you a way to talk about it, but to understand why, you need a solid example.

Lets say hypothetically you're working on this product object and the product object needs a datasource object (part of an ORM) to serialize itself to and from the database. Now various different people will argue back and forth over the best way to get that datasource object into the product object, but at the end of the day, why should it matter? If you're using direct references to variables.datasource then your object is absolutely dependent upon the datasource being stored in the variables scope. If you're using method calls, then your object isn't dependent on anything at all - the method call can get the datasource from the variables scope, from the this scope, from the request or application scopes or it can get the datasource object from another composed object. The rest of your object doesn't care. And the fact that it doesn't care makes it easier to stub alternative or replacement objects for tests - or to extend the product object for other purposes if they happen to come up.

Here's some code:

<cfcomponent displayname="product">
<cffunction name="dosomething">
<cfset var ds = variables.datasource />
</cffunction>

<cffunction name="somethingElse">
<cfset var ds = variables.datasource />
</cffunction>
</cfcomponent>

Now if you need to extend that product in order to store the datasource somewhere else like say in another composed object, how would you do that? It would force you to rewrite each method that references variables.datasource.

Compare to:

<cfcomponent displayname="product">
<cffunction name="getDatasource">
<cfreturn variables.datasource />
</cffunction>

<cffunction name="dosomething">
<cfset var ds = getDatasource() />
</cffunction>

<cffunction name="somethingElse">
<cfset var ds = getDatasource() />
</cffunction>
</cfcomponent>

Same question - if you need to extend the product object and you need to change where the datasource is stored, how many methods do you have to rewrite? Answer - just one. Which is one of the basic principals of good OO design, that it should be possible to make broad changes in a surgical way with very little code.

7 Comments

I tend to go one step a little further and pass in the dsn for any data accessing object. That way, you can actually decouple an object from it's application if it's contained in one. It's great help for unit testing as well, since you can pass in a different data source if the object is called from a test case.

After a while, I think we just gravitate to a certain style of OOness. For me, I kind a like having value objects that are completely unaware of any persistence technology at all. I wind up with a sort of anemic design on my value objects because, lets face it, a lot of web apps deal primarily with storing and retrieving data. There isn't a lot of behavior going on.

If I want to save one to the database, I generally pass it into it's corresponding DAO and it takes care of the work. When I instantiate a DAO, I've just gotten into the habit of its Init() function always taking a dsn as it's first (and usually only) parameter.

29 Comments

Getting back to the point that Ben made about 5 hours ago, I agree that OO design articles could definitely be better written. I think part of the problem is that Java's conception of OO has become enshrined as the one true way.

In Java, there is no prototypal inheritance, multiple inheritance, or dynamic typing. So thinking in Java will not guide you through using these constructs in an object-oriented way. The Java community also tends to favor flexibility at the cost of complexity. Completely understandable in many cases, but hardly a given in all cases.

OO existed before Java, and thrives in programming languages that were developed after Java. Python, Ruby, and PHP are on the ascent. They share with ColdFusion a dynamic approach to typing that renders many of the rockstar design patterns pointless.

Don't get me wrong, I think Java rocks. I just see no point in speaking German when I'm at an Italian restaurant. When I'm coding in ColdFusion, I should be able to think in object-oriented ColdFusion. And to help me along the way, I would love some writing on the subject that wasn't so beholden to Java's worldview.

15,848 Comments

@Rick,

I might agree with the overhead / complexity of adding unnecessary getters and setters if they are coded by hand. However, as in my case, the actual methods are generic and can are automatically scaled out using the VARIABLES.Instance collection. They are only overridden when necessary by concrete get/set methods. As such, at least in my case, they add no overhead.

As far as internal getDSN() methods, I don't actually do that. I just threw that out there because I know many people do. I have never run into a problem with referencing my own DSN value internally... but then again, I am new to this stuff.

@Sean,

As Hel Helms has told me many times, I have to stop thinking about the database. However, as most of our applications are really about *data* and not so much about behaviors (until you get into more complicated apps), its hard to stop thinking in terms of data even if you can stop thinking about the database.... still working at it.

@Joe,

Ha ha, well played ;)

@Brad,

Back in the day when I missed with .NET / C# I think it had a similar thing. Seemed cool.

@Rob,

You make a good point. We need to use a measuring stick that which is important and relevant, not just some buzz word or "key phrase" that we picked up. I am sure I heard the phrase "God Object" somewhere and it stuck in my mind as something that was very important. I think this is both the power and the danger of a meme.

@Ike,

You make a good point. I think this is perhaps one of the places where someone could invoke YAGNI and just argue that if things needed to change then it would be a simple refactor: add function and do find/replace for DSN references in the same file. Of course, the more complicated the inheritance chain, the more of a pain that would be.

I have not used internal references via methods much and am very new to this stuff, so I cannot really speak from experience.

15,848 Comments

@David,

Nicely said. Perhaps it would be useful for me to look up OO articles that center around PHP to get another point of view. Or perhaps find one and port it to CF.

9 Comments

@Ben

A "God Object", the way that I understand it, is effectively cloaking procedural code in an OO "wrapper". One big object that does way too much stuff. I believe it's widely considered to be an anti-pattern.

15,848 Comments

@Rob,

Thanks. I guess I just assumed that as an object got bigger it was a marker that it was taking on too much responsibility.

9 Comments

I don't think you're entirely incorrect. I think it can be an _indicator_, but it isn't necessarily some kind of de facto design error. If my objects get larger than I'd like, I'll often root around for refactoring opportunities, but I don't lose any sleep if I don't see any.

16 Comments

I agree with Rob. Object size (in terms of lines of code, methods, or both) is one way of identifying code that may be more of a problem to maintain in the future - more defects, defects harder to fix - but it doesn't mean you have to refactor the object. Sometimes the solution you need requires a big object; other times, it's simply not feasible in your environment to break up the object. (This could be related to YAGNI, particularly for smaller projects and smaller teams.)

Ben, I think your general question is fairly common in software engineering. "How much X do I need?" where X can be pretty much anything. It's easy enough for someone to say "Just enough and no more", but that isn't a practical answer ... if we knew how much that was, we'd not have to ask.

Maybe one way those questions could be answered better would be through little bites: posts that give you little bits of what you want, then ask "Is this enough or do you need more?" Adrian Moreno's series on OO and CF was pretty helpful to me (although I have a pretty decent OO background) ... I think his series starts with this post: http://www.iknowkungfoo.com/blog/index.cfm/2007/8/22/Object-Oriented-Coldfusion--1--Intro-to-Objectcfc

78 Comments

@Ben -- okay, so here's the deep thought, re: find/replace and the inheritance chain. ;) You mentioned not having had any problems referencing variables.dsn in your objects and that's fine and actually expected. The issue of an internal getter like that actually is one that comes up as a matter of simplifying the inheritance chain.

Your suggested YAGNI argument that you could just find/replace in the one CFC will work of course in the event that you don't actually have an inheritance chain. If you're not extending anything. But that's actually the reason behind having the internal getter in the first place, because once those 4 lines are in place, then you never have to worry about the inheritance chain.

Different objects in the inheritance chain can get the object from whever they need to without bothering or modifying the other objects.

In some cases where you saw for example a problem with an object someone else wrote, if they used an internal getter, you could "fix" their object without editing it, simply by writing another CFC that extends it and overwriting their internal getter. (About 6 lines of code.) By comparison, if they didn't have an internal getter, you would be forced to actually perform that find/replace on their code which would mean that if they later released an updated version with new features, you'd have to do that same find/replace all over again. If they had the internal getter and you just wrote a subclass with a new getter, then there's no new work because you've already done the work when you wrote your subclass.

Does that help clarify the example?

There's a quote from Brian Foote's "Pattern Labyrinth" that talks about this sort of thing that I put on the onTap framework home page. Here's the quote:

The true value of object-oriented techniques as opposed to conventional programming techniques is not that they can do things conventional techniques can't, but that they can often extend behavior by adding new code in cases where conventional techniques would require editing existing code instead. Objects are good because they allow new concepts to be added to a system without modifying previously existing code. Methods are good because they permit adding functionality to a system without modifying previous existing code. Classes are good because they enable using the behavior of one object as part of the behavior of another without modifying previously existing code
- The Treaty of Orlando [Stein et. al. 1988]

And here's the Pattern Labyrinth:
http://www.laputan.org/selfish/selfish.html#ProgrammingByDifference

15,848 Comments

@Dave,

I have seen Adrian's post, but have not looked through it in some time. I'll give it another read through, thanks.

@Ike,

Ok, I'm sold. That was a very good explanation.

15,848 Comments

@Ike,

With ColdFusion 8, it might be nice to have a method that returns more than just the DSN source, but also the username and password as a struct. This would be very useful for attributeCollection:

<cfquery name="qTest" attributecollection="#THIS.GetDSN()#">

78 Comments

@Ben, thanks. :) And re: return a struct, yes absolutely. In my particular case with the DataFaucet ORM that's not such an issue simply because my application only has one pair of <cfquery> tags... (in the ENTIRE application). :P Now, admittedly the majority of solutions, even those using other ORM tools don't abstract cfquery *that* much. (Okay, you got me, there are 2 pair.) And so in my unusual case I would rarely need that struct, but in the case in which I were working on a more traditional OO CF app which had cfquery tags in the model objects (or ORM objects) then I would think that would be an excellent solution for CF8. Imo it's shorter and much more elegant than writing out datasource="#ds()#" username="#usr()#" password="#pwd()#" everywhere. :)

78 Comments

Although I would probably make getDSN() a private method rather than public in that case (even though with DataFaucet, getDatasource() is often public).

15,848 Comments

@Ike,

This is a poor design choice, but I would make it public simply because its easier to type THIS vs. VARIABLES. Although, let's be honest - once I write the first CFQuery, its pretty much copy-n-paste going forward :)

78 Comments

@Ben - LOL ... umm... I've commented on Pete's blog a few times. :) Nah, in my case DataFaucet builds queries as an array where some of the elements are structs (for cfqueryparams) and then they get piped through that cfquery tag pair... So instead of cfquery tags I may have a variety of syntax like

ds = getDatasource();

query = ds.query_from_tblProducts_where_price_lte_50();

although I'm not entirely sold on that syntax yet -- it's in there, but I'm still not really using it, partly because it's onMissingMethod driven which means CF8 and partly because it's more lexing & parsing to get the query

or I might have

query = ds.getSelect("*","tblProducts").filter("price",50,"<=").execute();

DataFaucet actually offers a wide variety of different ways to build queries, those are just a couple examples off the top of my head. And having an object abstraction allows me to do some pretty sophisticated stuff with it like statement.andOrFilter("author,subject,message",form.search) to perform an and/or keyword filtered query across those three columns. That way I can just reuse the and/or keyword feature, rather than having to rewrite it when I want to create another search tool for another site or application. That's the sort of thing you're not going to find in the current versions of any of the other data access tools for CF as far as I know. I think Mark may be working on (or at least thinking about) an and/or keyword tool in a future release of Transfer.

Speaking of which I'm giving a presentation to the DFWCFUG tonight -- not sure if it will be more on DataFaucet ORM or more on SOA. But then I'll also be giving another DataFaucet presentation for the CF Meetup group that Charlie Arehart manages on Thursday.

http://carehart.org/blog/client/index.cfm/2008/10/13/cfmeetup_Oct_16

78 Comments

@Ben - Oh, I don't bother with scoping internal method calls. If it's a private function, I just call functionName() -- For that matter I don't generally scope calls to public methods either. I only scope function calls if I need to get at something "from the outside". I can't remember what the circumstance was, but very occasionally you can run into a situation where CF needs to access a function from "this" instead of from the variables scope. I just can't remember what the circumstance was the last time I ran into that.

78 Comments

I think I somehow Missed Rick's earlier comment when I was reading over them... part of the advantage of a getter (and I still disagree that they need to be explicit getters or that explicit getters are faster or easier to work with), is that it makes it easy to lazy-load values if you find some need to do that.

If you started out setting the variable in the this scope, then when you want to lazy load it, you have to suddenly replace any instance where that variable is accessed by an external object, and depending on how consistent your code is (and lets face it, even as programmers, we're still human and our code still tends to be somewhat inconsistent), it can be a challenge to find all those instances to replace them with find/replace tools in your IDE.

If you've coded your app in such a way as to have a getter in advance, whether explicit or generic (i.e. getValue) then you can easily start lazy-loading once you realize a need without altering the external interface to your object at all. So you only have to change that 1 function, not any of the places where the value is references from external objects.

That being said, the generic getter/setter pair takes that encapsulation a step further. Rick said "you're already changing your api anyway"... but that's not the case if your external get/set methods are generic. If you're using generic external get/set methods, then external objects don't have to do anything different to access those values. And it even creates an in-road for "graceful degrading" when integrating different objects together.

Just as we talk about gracefully degrading for screen readers, we can also design our objects to degrade gracefully. Say your object adds a new feature and that new feature is dependent upon a new property. And lets say that this is an object is something that other people will use like -- I'll take Steve Bryant's DataMgr as a random example. DataMgr is an amazingly small tool for what it does, being under 50MB. Now I don't personally know what he does for get/set with DataMgr (I haven't really looked), but lets say hypothetically that he added a new feature and it depended on a property that was set via a traditional setPropertyX() method. Because he used setPropertyX() his external interface changed and there's a sudden version dependency introduced.

Now some *rare* programmers might be really clever and choose to check and see if DataMgr has that setMethodX() function before trying to use it - or more likely simply check the version number if Steve exposed it. But the vast majority of programmers won't, they'll just use the method and if it blows up, it blows up and they'll say "oh you need XXX version of DataMgr"... which may or may not be compatible with other things in your app. Simply by using a generic setValue(x,y) method instead of an explicit setPropertyX() he could immediately end-run around a lot of potential version dependencies by allowing the code to function if the new feature is non-critical (as they often are). So you don't get the pretty pictures or whatever it is that was added, but it's not version dependent and doesn't blow up when somebody sets the as yet un-used property.

It also works both ways. If he later deprecates a property, he can ultimately remove that property eventually also without changing his public interface any and thereby avoiding the introduction of yet another version dependency when the property ultimately goes away.

Not sure how coherent this comment is. I'm a bit tired. :P

78 Comments

I should have said "that doesn't *have to be* the case if your get/set methods are generic". Rick showed an example from BlogCFC where that's not true because it throws an error - but they can be designed in such a way that they degrade gracefully, effectively reducing version dependencies and keeping the external interface from changing.

15,848 Comments

What do people think of using getters in a data persistence object (DAO or Service or what have you)? Using things like this:

UPDATE
. . . . [table]
SET
. . . . name = #ARGUMENTS.Bean.GetName()#
WHERE
. . . . id = #ARGUMENTS.Bean.GetID()#

Do you feel like this use of Getter() methods is an indication of poor OO design?

If so, I can think of two solutions:

1. All data persistence should be in the object that contains the data.

2. All data should be passed to persistence object as simple values. For example:

DAO.Save( id = 4, Name = "Ben" )

... rather than passing in the bean. In my mind, this is a bit akin to the "Builder" example I see in so many Java examples.

I am struggling with this and would love feedback. Does the use of Getter() methods in your SQL indicate poor OO design?

31 Comments

@Ben

I think either approach is valid depending on what you are trying to accomplish.

Personally, I typically go with option 1 for two reasons:
- to allow more flexibility in what is passed to the DAO.
- so the object can know what DAO created it.

For the first reason - An example would be having multiple records in the object, allowing all records to saved with a single SQL transaction http://cfblog.co.uk/index.cfm/2007/11/13/Multiple-inserts-ORACLE-MYSQL-MS-SQL

For the second reason - so I can use bean.save(), and bean.validate() to automatically pass the bean to those functions in the DAO as the default save and validate (e.g. are the values the correct data types to be placed in the database)

For an example of how I've using this approach:

I've standardized on using what I call Data Table Objects for my default object when getting data from or to a DAO.
The DTO is an object containing: a query object, iterator functions, getter/setters for each field and a hasChanged tracker for each record.

I use the getter/setters to manipulate the data.

But for working with the data in my DAO, my DTO contains a generic function called getFieldValue("columnname") Which returns the value of the specified query column of the current row.

The column names match the field names in the database, so:

When my DAO initializes, it uses the DBInfo function to get the column names, and datatypes for the table.

I can then create my SQL statements by looping through the field names:
#LOCAL.field# = <cfqueryparam value="#ARGUMENTS.DTO.getFieldValue(LOCAL.field)#" cfsqltype="CF_SQL_#THIS.getFieldCFSQLType(LOCAL.field)#">

The function getFieldCFSQLType() returns the name of the cfsqltype that maps to the data type in the database.

15,848 Comments

@Steve,

I am not clear as to what you are talking about in regards to DAO and DTO. Are you saying that the DAO is composed in your DTO? Or are you saying that the DAO functionality is actually part of the DTO?

I guess the better, more important question is, when your run your SQL statements, where are you getting your data from? Is it internal to the current object:

ID = #GetFieldValue( "id" )#

... or is it in some other object:

ID = #DTOInstance.GetFieldValue( "id" )#

15,848 Comments

@Steve,

Oops, I didn't realize that I wasn't scrolled far enough down. I missed the last few lines of your post in my email.

So, it seems you are passing int he DTO as an argument to the DAO:

ARGUMENTS.DTO.GetFieldValue( ... )

So, I guess, ultimately, you are saying that using accessor methods (Get() style methods), when used inside of a data access object should not be considered an OOP design flaw?

31 Comments

@Ben,

I guess that is what I'm saying.
I alway's seem to have some form of a getter function for pulling the data out.

I've got an object containing a set of data (my DTO)
I can put data in to a DTO by
- initializing it with a query object, or
- iteratorating to an existing or new row and using a setter

When I want the data out I can:
- use the field specific getters. (e.g. getName())
- use the generic getter (e.g. getFieldValue("fieldname"))
- get the query object (e.g. getQuery())
- get a serialized version of the query object (e.g. getJSON())

For uppper level code I tycially use the field specific getters to ensure a stable interface.

For my data access object

When I pass the DTO to DAO (e.g. myDAO.save(myDTO) I have three options to access the data it contains:
- reference the query directly
- use the generic getter - getFieldValue
- use the field specific getters

If I go for the first option I lose the benifit of the iterator functions,
if I go with the third, I have to write add and update SQL funcitons for (see the end of the last comment for how I use this to create generic add and update funcitons shared by all my DAOs).

15 Comments

I've been down this baffling road of Coldfusion OO years ago. It's just so much easier when a language or framework just forces you to do it a certain way isn't it? Now I'm working on a project where performance takes a front seat and certain CF OO mimic-niques simply aren't viable.
But for those that just love OO at the sacrifice of practicality, it's my opinion that the DAO is specifically for working in depth (CRUD) of a single fully-encapsulated object. In that sense you are supposed to pass in an object or a snapshot of the object's data (bean). Either way you'd use getters to grab the data. My vote is for "object in, object out" or the first option you posted.
Your question makes me feel that we don't have our terminology clearly defined yet. It might help clear things up if we had an idea of what components you're creating and what they're going to be used for. I use "ObjectListener" (for event announcement based apps) or "ObjectService" for business logic, "ObjectBean" for beans, "ObjectDAO" for in CRUD operations, and "ObjectGateway" for pulling more than one row of data or even a single row query.
The biggest questions come up when I try to figure out what to do when I just want to update a few fields in the database, or store/update more than one object at a time. Since we're being strictly OO, then for small updates I should probably pull then entire object (say it has 20 properties) from the table(s), use setters to update a property or two and then store it back in the DB with my DAO. If I wanted to create multiple rows I should probably create each object separately and save them one by one with my DAO.
If we take it a step further, even when I would want to grab one property from an object, say it's name, I should still grab all the data from the db, put it into a fully-encapsulated object and read it from there.
I just feel that at some point, strict OO is just bad CF and using the native objects that cf gives you, like queries and structures, just makes more sense.

78 Comments

Hal. You mean the pattern labyrinth article. Thanks. :) I certainly thought it was an interesting read.

15,848 Comments

@PJ,

I agree that there some aspects of OOP are simply not good ColdFusion practices (at this time). As I am learning OOP, I accept this, but at this time have not gone down that road (the balance between OO and CF-Strengths/Weaknesses). Right now, I just want to work on theory. Then, I will pull back the throttle and work on optimization.

10 Comments

I have read g/s actually represent a failure to encapsulate and create unnecessary coupling. Could someone explain this view to me?

21 Comments

I beleive the gist is this-- You should ask objects to do things for you. You shouldn't ask an object for a bunch of information and then do something with it. Google "anemic domain model"

I'm not saying it's bad to have gets and sets, but if that is ALL your objects ever have then where is your business logic?

Warning: you are on the edge of a highly debated/opinionated discussion about fat services, and skinny beans, etc. I would recommend reading all the comments on this entry for more opinions.

15,848 Comments

@Mike,

@Brad is right - this is hotly debated topic. And, when you get into things like Code generation and scaffolding, I think it gets even more hairy. It's one of those things where you can't make it black and white or it just doesn't make any sense.

Basically, the gist is, if all you ever do is get/set data out of and into objects, then you might as well be using Structs. The point of an object is that it is supposed to DO things.

Now, that doesn't mean that *I* have a great grasp over OO architecture; that's simply what I've picked up in theory along the way.

26 Comments

@Brad Wood,

Yes, consider it "Tell, don't ask!"

If you have code that does get, get, get, do something, set, set, then do something probably belongs inside the object, not outside it. In other words, you should tell the object to do something and not care what data it has or how it does it.

Does that help folks?

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel