In my last post on object oriented data validation and error message translation, there were some really good conversations about factoring out validation logic such that different scenarios could validate the instance data in different ways. This morning, I tried to apply that methdology by factoring out my Girl.cfc and Clothing.cfc validation logic into "validation behaviors". I am not sure what the exact definition of a behavior is, but I believe it is an ecapsulated piece of swappable functionality that implements a given interface such that it can be utilitzed by another object within an algorithm. In this case, the validation interface is only that the validation behavior has a Validate() method that takes a reference to the target bean's instance variables.
To do this, I updated the Base.cfc to have a ValidationBehavior variable and a Validate() method. This way, the validation can be passed off to the validation behavior while still allowing the object to appear as if it were a "Smart" object that knows how to validate itself:
Launch code in new window » Download code as text file »
Notice that we also have the method SetValidationBehavior(). This method allows us to determine at runtime which set of validation logic will be used to validate our objects. This logic might need to change depending on the current user or touch-point in the application and so, we can swap one behavior out for another as we see fit. The Validate() method, then passes the bean's instance data off to the Validate() method of the valdiation behavior instance.
Once I had the concept of a validation behavior, I had to start building the validation behaviors for our Girl.cfc and our Clothing.cfc. To start with, I created a BaseValidation.cfc which all the Validation behaviors would extend:
Launch code in new window » Download code as text file »
While I am not really taking much advantage of it at the moment, this base validation object could house utility functions that would be common to all validation behaviors.
Notice also that the BaseValidation.cfc ColdFusion component has a Validate() method that must be overriden. This method method is the full definition of the "Validation Interface." It is the now the job of the individual behaviors to implement that abstract method. In this go-round, implementation simply meant taking the validation logic out of the beans and putting it into the individual behaviors.
Here is the ClothingValidation.cfc:
Launch code in new window » Download code as text file »
... and the GirlValidation.cfc
Launch code in new window » Download code as text file »
As you can see, I didn't really change this logic in any way from the previous post. The only real difference is that the validation logic is working on the passed-in Properties argument rather than working directly on the instance variables. This methodology allows us to swap out behavior methods at run time without having the beans become too highly coupled to the behavior implementations.
This leaves us with fairly simple beans. Our Girl.cfc and Clothing.cfc ColdFusion components now have nothing more than their own Init() methods and default instance data values.
Here is the new Clothing.cfc:
Launch code in new window » Download code as text file »
... and the new Girl.cfc:
Launch code in new window » Download code as text file »
As you can see, there's really nothing going on in the beans anymore. I was afraid that this mean that they were becoming quite anemic, but as Jeff Chastain reassures me, factoring out validation logic does not necessarily make a bean anemic (as it can have lots of other related functionality).
Ok, now onto the the action page that actually handles the form submission and the data validation. I have updated this in a few ways. For starters, I have added the validation behaviors from above. Secondly, I have moved the error flags translations out of the controller and into a separate display file that was more view specific. For some reason, I didn't like the error message translation code in the controller; it made the code look too cluttered. Moving it into a separate display file made it feel more coupled to the View (which is really what makes more sense), and makes the code seem more organized in general.
Launch code in new window » Download code as text file »
Notice that as we are processing the form data, we create an instance of the GirlValidation.cfc as well as the ClothingValidation.cfc and inject those into the appropriate instances before we Validate() the data. As a side effect of this process, we do get something nice to happen - we use one instance of the ClothingValidation.cfc ColdFusion component to validate all of our Clothing.cfc instances. Since the validation logic is not stateful and depends only on the data that is passed to it, we have now created one instance of the validation code, rather than N instance for N pieces of clothing. Plus, this means that we can cache singletons of our validation logic in the APPLICATION scope, or even the SESSION scope, and then use those at runtime to do the validation. Something about that is deeply satisfying.
The Validate() method returns a property-indexed struct just as it did in the previous blog post. This time, however, rather than handling that logic in the controller, I have moved the error translation into a separate file, dsp_form_display.cfm, that deals just with error messages for just this view:
Launch code in new window » Download code as text file »
This file is then included more cleanly in the actual form dispaly page, dsp_form.cfm:
Launch code in new window » Download code as text file »
So that's my attempt at factoring out the validation logic into a behavior-based system. Despite the fact that we have a seemingly large number of files to handle such a small piece of functionality, there is something very appealing about it. You still get to leverage the "Smart Object" scenario where the Model knows how to validate itself, but at the same time, you also get the benefit of being able to swap out the validation logic if needed. This really leaves your code "open" for extension and "closed" for change, which, if you have read any design pattern books, is something we are definitely shooting for.
I also liked moving the error flag to message translation in to a sepparate display file. This created a nice, encapsulated piece of functionality; and, it didn't feel like it was stepping on anyone else's toes.
What I am curious about, though, is what happens when the Validate() method needs data outside of itself? What happens when the validation is a business rule that involves other parts of the system. For example, what if the validation needed to make sure that no other Girls in the system existed with the same first and last name combination. At that point, we need some sort of a Girl Service. But where does that get passed in? I assume not as part of the Validate() method as its interface only expects the Instance variables. Then, perhaps, other helper methods such as SetGirlService() which will set an internal variable to be used during validation? More to come on that I suppose.
But, is this starting to get a bit overkill for such a simple form validation? At face value, of course. But, again, we have to remember that the benefit of object oriented programming is not so much in the one-off, single tasks; it's in the long term maintenance and scalability of our applications. So, in that respect, we have something cool happening here.
Download Code Snippet ZIP File
Comments (4) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
Creating Semi-Secure File Downloads Without Using CFContent
HostMySite.com Has The Best ColdFusion Hosting
Ben, I'd recommend using a Factory to create the correct Validation object rather than manually having to create it and pass it in. That would get very difficult to maintain.
Regarding cases where the Validator needs information outside of itself, this can be handled using a Custom Validator and managing it with ColdSpring. For example, your generic Validator may handle general things like a field being required (not empty), a field being less than or greater than a certain number of characters, etc. But you could also have custom validators for cases where more data is needed. Say you want to check user name uniqueness. You may have a UserValidator that extends BaseValidator and can handle this check. You'd have ColdSpring inject a reference to a UserService, and the UserValidator could call userService.isUserNameUnique(userName).
A nice side-effect of this is that if you implemented some sort of JavaScript/AJAX validation in the HTML form, it could make a call to the same service method to alert the user right in the form that the user name was already in use. Hopefully this makes sense.
Regards,
Brian
Posted by Brian Kotek on May 13, 2008 at 9:08 AM
@Brian,
That stuff makes sense, but I want to keep it as simple as possible for as long as possible. I want to understand the concepts and the problems before I even think about learning something additional like ColdSpring to help tie it all together. From everything I've heard and read, ColdSpring and IoC frameworks makes life much easier, but I have to learn to walk before I can learn to run :)
I like what you're saying about the user service and how that can be used by various forms of validation. I sort of understand the concept of dependency injection, but I don't know if I understand the practical implementation. Do you need to have a Setter / Getter for each dependency that you want to inject?
For example, using your comment, would my validation behavior have methods for:
GetUserService()
SetUserService( objService )
Posted by Ben Nadel on May 13, 2008 at 9:19 AM
"Do you need to have a Setter / Getter for each dependency that you want to inject?"
Writing a setter for each dep. is one way ColdSpring can work and it works well with 'auto wire' where CS will look at the set'ers for a CFC, and call them automatically with the correct CFC.
You can also use 'constructor' injection, where all the deps are passed into an 'init' method, but I prefer auto wire as it keeps the XML size down and means the CFC is the documentation, not the XML file.
Posted by Tom Chiverton on May 13, 2008 at 9:39 AM
That is all true, but the (arguably) biggest reason to favor setter injection over constructor injection is that it allows ColdSpring to easily resolve circular dependencies (where CFC A depends on CFC B, but CFC B depends on CFC A).
Yes Ben, that's exactly right. You'd have a setter for any properties that you want ColdSpring to inject for you. So if your UserValidator CFC has a setUserService method (with userService as an argument), ColdSpring will look for a matching UserService bean that it is managing and inject it using that setter. This is "autowiring by name". For more clarity, you can explicitly declare the dependency which is generally preferred. In the XML you'd just have:
<bean name="userService" class="app.cfcs.UserService">
<bean name="userValidator" class="app.cfcs.UserValidator">
<property name="userService">
<ref bean="userService" />
</property>
</bean>
Posted by Brian Kotek on May 13, 2008 at 9:52 AM