Showing posts with label oop. Show all posts
Showing posts with label oop. Show all posts

Friday, December 18, 2009

Using onMissingMethod to Treat Properties More Like... Properties

Since I didn't come from a Java background, I'm relatively new to OOP best practices. One thing that I've grown to loathe in my short time working with objects in Hibernate is having to go through getters and setters to access my properties:


<cfset user = new User() />
<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />

<cfoutput>
My name is #user.getFirstName()# #user.getLastName()#
</cfoutput>


I'd much rather prefer true implicit getters and setters a la Groovy, ActionScript, C#, etc...


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
My name is #user.firstName# #user.lastName#
</cfoutput>


After working with jQuery a little more, I really like the convention used on their event helpers, like change() and change(fn). If you pass in an argument, it binds a new observer. If you don't pass in an argument, it triggers the event.

I was able to apply similar logic to my entities in ColdFusion with the help of onMissingMethod() inside a base class that my entities extend.


component {

public void function set(required string property, required any value) {

if (structKeyExists(this,"set#arguments.property#")) {

if (!structKeyExists(arguments,"value") || isNull(arguments.value) || (isSimpleValue(arguments.value) && arguments.value eq "")) {
evaluate("set#arguments.property#(javacast('NULL',''))");
}
else {
evaluate("set#arguments.property#(arguments.value)");
}

}

}

public any function get(required string property) {

if (structKeyExists(this,"get#arguments.property#")) {
local.value = evaluate("get#arguments.property#()");
}

if (!structKeyExists(local,"value")) {
local.value = "";
}

return local.value;

}

public any function onMissingMethod(required string missingMethodName, required struct missingMethodArguments) {

if (structIsEmpty(arguments.missingMethodArguments)) {
return get(arguments.missingMethodName);
}

set(arguments.missingMethodName,arguments.missingMethodArguments[1]);

return this;

}

}



Now if I pass in an argument, it sets the property. If I don't pass an in argument, it gets the property, leaving me with the following code:


<cfset user = new User() />
<cfset user.firstName("Tony") />
<cfset user.lastName("Nelson") />

<cfoutput>
My name is #user.firstName()# #user.lastName()#
</cfoutput>


And in case you didn't read the code, also included in the base class are generic get() and set() methods that relay calls to the actual property getters and setters in order to maintain proper encapsulation.

Tuesday, December 1, 2009

ColdFusion Reference Wikis

I recently stumbled upon a couple ColdFusion wiki sites that I thought were worth mentioning.

Both sites were created by Kevan Stannard, a ColdFusion developer from Australia. While the sites appear to be a work in progress, there's still quite a bit of useful, thorough, and user-friendly content to read, as well as links to other resources on the web. His personal development blog has some good posts that are worth checking out too.

I don't know Kevan and honestly I hadn't heard of him until running across his sites, which I find a little surprising considering the quality of the content. It was pretty refreshing to see some common programming techniques applied in ColdFusion and explained with a human touch. I wish I had found these sites when I was first starting out.

Anyways, I just thought he should get a quick shout out for his efforts.

Thursday, October 1, 2009

Implicit Getters and Setters

While the addition of implicit getters and setters for properties in ColdFusion is really nice, I'm still a little jealous of the how they're handled in Groovy.

Take the typical User object.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />

</cfcomponent>


In ColdFusion, you would interact with this object like such:


<cfset user = new User() />
<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />

<cfoutput>
Hello, my name is #user.getFirstName()# #user.getLastName()#
</cfoutput>


Pretty basic. Now how would that look if ColdFusion had implicit getters and setters like Groovy:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.firstName# #user.lastName#
</cfoutput>


Much cleaner. Now before you say it's just accessing the properties directly (as if they were stored in the this scope), there's more to it than that. When you're accessing the property, the call is still being routed through the implicit getters and setters. This means you can still add your own custom logic to the properties by overriding their methods.

To demonstrate, let's add a 3rd property to the User component, fullName, and add our own getter.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />
<cfproperty name="fullName" />

<cffunction name="getFullName">
<cfreturn getFirstName() & " " & getLastName() />
</cffunction>

</cfcomponent>


Now let's update our code:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.fullName#
</cfoutput>


Even though it looks like we're accessing the fullName property directly, it will still be routed through getFullName(), which should output "Tony Nelson".

I've gone back and forth on whether or not this is a good thing. On one hand, you're calling a method even though it doesn't look like it. On the other, it's a lot cleaner. Some people may argue that access to properties should remain hidden behind Accessors (getters) and Mutators (setters), but in truth they still are.

Besides, if our goal is to have rich business objects that have both properties and behavior, shouldn't we be able to treat the properties like they were just simply properties and nothing more? To me, having a getter implies there's some additional behavior required in order to retrieve a property, which in most cases there isn't. What are we really gaining by masking the call to a property behind a method?

Saturday, August 8, 2009

How smart should an object be?

Since I'm relatively new to OOP, one thing I've been struggling with lately is how smart should I make my domain objects. Should an object know how to save itself? Take the following code for example:


user = new User();
user.setFirstName("Tony");
user.setLastName("Nelson");
user.save();


This type of code follows the Active Record design pattern, where domain objects contain your CRUD operations.

Hibernate, which acts as a Data Access Object, takes a slightly different approach. Rather than having the object save itself, Hibernate performs the CRUD.


user = entityNew("User");
user.setFirstName("Tony");
user.setLastName("Nelson");
entitySave(user);


Since Hibernate has become the ORM of choice in ColdFusion, I've decided that an object should not know how to save itself. Now with that in mind, should an object know how to populate itself? Consider the following code:


user = new User();
user.setFirstName(arguments.firstName);
user.setLastName(arguments.lastName);
user.setEmail(arguments.email);
user.setPassword(arguments.password);
user.setBirthDate(arguments.birthDate);
user.setGender(arguments.gender);
user.setHeight(arguments.height);
etc...


Pretty sure we've all seen code like this, where you're just taking all the arguments and calling their corresponding setters by adding "set" in from the argument name. Wouldn't it be a lot simple to just do:


user = entityNew("User");
user.populate(arguments);
entitySave(user);


But again, since a user shouldn't know how to save itself, it probably shouldn't know how to populate itself either, which gives us:


user = entityNew("User");
entityPopulate(user,arguments);
entitySave(user);


Mmmm... entityPopulate() would be a handy little tool. Now what about validation? Same theory applies...


user = entityNew("User");
entityPopulate(user,arguments);
errors = entityValidate(user);
if(ArrayLen(errors) == 0) {
entitySave(user);
}

Well it's short, although I'm not sure it's entirely clear what we're saving since it's so generic. But again, maybe that's a good thing. Ideally, you could get rid of all the "entity" stuff and make a really generic Service that looked like this:


user = new("User");
populate(user,arguments);
errors = validate(user);
if(ArrayLen(errors) == 0) {
save(user);
}


That almost looks as good as pseudo-code, with the only thing that's a little code-y being ArrayLen(errors). Speaking of which, there should be a generic length() function that can accept an array, query, struct, or string and return its length, regardless of data type. Man, that would be pretty sweet. Then the code would look even shorter.


user = new("User");
populate(user,arguments);
errors = validate(user);
if(length(errors) == 0) {
save(user);
}


Seems pretty clean. Just for fun, how would that look using smarter objects and a little method chaining?


user = new("User").populate(arguments);
errors = user.validate();
if(length(errors) == 0) {
user.save();
}


Not too shabby. And now you see my struggles.