Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Feb. 2009) with: Aaron Foss

OOPhoto - Another Attempt At Learning Object Oriented Programming (OOP) In ColdFusion

By Ben Nadel on
Tags: ColdFusion

One of the nice things about coming back from a conference like CFUNITED is that you always feel inspired to go out and learn all the stuff that you don't know. One beast that I have been wrestling with for a long time is Object Oriented Programming (OOP). I have decided that I am gonna take that inspiration from CFUNITED and focus it, once again, on this subject in hopes of finally being able to wrap my head around it. There are several big constraints that I have not been able to even get close to understanding; of these, data validation and error translation are the biggest hurdles.

So, to help me in this exploration, I have come up with a dummy project - OOPhoto. This title is the combination of "OOP" and "Photo" - see the "P"s overlap (yeah, I'm mad clever that way). OOPhoto is going to be a very simple photo album application. No users, no security - just galleries, photos, and comments. This way, I will keep it as simple as possible while, at the same time, including many aspects of object oriented programming such as object composition and services.

Now, just because this is an exploration of Object Oriented Programming in ColdFusion, it doesn't mean that I can ignore everything else. Just as with all projects, this one will utilize the Interface Driven Architecture (IDA) methodology. If any of you were lucky enough to attend Clark Valberg and Hal Helms' CFUNITED presentation, Prototyping for Smarties, you will remember that creating the interface is, in many ways, the most important part of a project and the only real way to ensure success. And so, as always, I will start off with the interface and then, based on the interface and the other project specifications, I will move onto defining the domain model.

I have taken a little time to throw together some Fireworks PNG files of the design I want to implement. Right now, these are just graphic files.

Home Page

The home page will give you two types of search functionality - keyword and jump code searching. I wanted to do it this way, not because I think jump codes are too useful, but rather because I envisioned these as two different services on the photo galleries. It will also show recent photos - another photo service.


 
 
 

 
OOPhoto Home Page - An Exploration Of Object Oriented Programming In ColdFusion  
 
 
 

Keyword Search Page

The search page will display a single page of results based on your keyword search; I don't think I want to deal with pagination at this point in the game - keep is simple, learn it, and then I can make it more complex later on.


 
 
 

 
OOPhoto Search Page - An Exploration Of Object Oriented Programming In ColdFusion  
 
 
 

Photo Gallery Page

The photo gallery page just lists all the thumbnails of images in the gallery.


 
 
 

 
OOPhoto Gallery Detail Page - An Exploration Of Object Oriented Programming In ColdFusion  
 
 
 

Photo Gallery Edit Page

The edit page contains very simple data. The most complex thing here was a drag-n-drop photo sorting mechanism that I have wanted to play around with.


 
 
 

 
OOPhoto Gallery Edit Page - An Exploration Of Object Oriented Programming In ColdFusion  
 
 
 

Photo Detail Page

The photo detail page will display a large image and any user comments. I like the user comments because it becomes a potential second layer of objects composition.


 
 
 

 
OOPhoto Photo Detail Page - An Exploration Of Object Oriented Programming In ColdFusion  
 
 
 

So that's the design I have so far. The next step is build those designs into an XHTML / Javascript prototype that can be used for usability testing. I think this application is simple enough that I won't get overwhelmed, but complex enough that it will give me a great insight into object oriented programming. I am trying my best not to think at all about the data persistence at this point. In fact, I think I might create the whole data persistence out of XML just to drive home the idea the interface and the domain model are ultimately much more important.

To close, I'd like to echo back some (paraphrased) words of wisdom that I have heard from the great Hal Helms:

  • Objects should be idealized versions of themselves.
  • Objects are not merely collections of data.
  • If your methods are getting very big, you're probably not doing OO programming.
  • If you're not failing 90% of the time, you're not aiming high enough.



Reader Comments

@Ben,

One of my earliest hurdles in the whole "grasping of OOP" thing, which Hal definitely helped define for me through his series of Occasional Newsletters, was getting past the data persistence. You are on the right track there: imagine, design, and architect the application without thought to the data. Start with interface and objects. Very difficult for those of us raised on the DB first and the interface to manage it second. If you don't, however, it greatly increases the difficulty of thinking of your objects and their necessary properties and methods as real objects (and not just mirrors of the DB schema).

Once you've got that conception in place, then the DB becomes little more than a mechanical exercise in determining what schema will accurately persist your objects' properties in a way that also offers high performance.

Reply to this Comment

Hey, good luck. One of the things I hope will become bigger in the next release is cfinterface. It's not even useful in the current implementation except in certain introspection cases. They don't even mention it in the devguide...

Right now people use cfargument type="any" to pass in cfcs. We should be using cfargument type="objName"

In normal OO languages, interfaces are huge... but in CF since we can be completely sloppy with our var type declarations, people don't have as much of a use. But for now, remember cfArgument type="interfaceName" will be needed...

Reply to this Comment

@JFish,

Yeah, that is something that he has tried to drill into my head for a while now :) It is a hard leap to make, but to be honest, I have been able to steer clear of it so far for this project. I have only thought of the design and tossed around some object composition ideas in my head. But, really, that's not even necessary till the prototype is done.

@Jim,

I understand what you are saying... but I think maybe there is something to be said about being able to leverage the dynamic nature of a typeless language, rather than worrying about making it fit into an old OOP model?

For instance, I am thinking of using a generic Get() and Set() method that takes a possible second value... like jQuery's attr() method:

attr( "id" )
attr( "id", 45 )

Here, the value type is not set or required in anyway. I am new to OOP, so I cannot say if this is a horrible idea, but it seems like a nice way to easily create dynamic setters and getters.

Reply to this Comment

Ben, if you are interested, once you have your version of this application working, it might be worth putting it up for others to take and implement their own version of the model. I know Hal has problems with ORMs like Transfer because they depend on the database to generate their objects, but they are also the best thing we have at the moment (until we get something like Hibernate that can generate a schema from a domain model). I personally might be game to take a crack at this using Transfer once you have the controller and view done, just to show that in many cases the reduction domain model "purity" may be worth the advantage of not having to hand-code all the queries, factories, and composition relationships. It seems to me that there should be a point where purity is balanced by pragmatism. It might be interesting to see if that is the case here. Thoughts?

Reply to this Comment

@Brian,

That would be wicked awesome :) Especially since I am so new to this, all the different points of view would be cool. Hopefully, this should be a fairly small app - only 5 screens, or whatever I had above, so I hope to get something out there soon.

Reply to this Comment

I would recommend that you at least follow the service layer pattern to create a stable API to your model. That way it will be easier for me or others to plug in alternate implementations, as well as easy for someone to write alternate UI for it (AJAX or Flex for example). You may already have been planning to do this but I just thought I would mention it.

Reply to this Comment

@Brian,

Do you mean (for example) a REST web service layer to all the actions? Or do you just mean a collection of service objects that perform actions on the system?

Reply to this Comment

No REST yet, I just mean that everything the model does is behind a service layer object. So if the controller needs the list of pictures it does something like <cfset pics = getPicService().getPictureList() />. The controller shouldn't be making any queries or calling domain objects directly. Everything should go through the service layer. Make sense?

Reply to this Comment

It's a good practice because it means your controller has no logic other than to handle page flow, and all business logic is kept behind a stable interface. So if you start off running your own queries and I later want to add in Transfer, I only need to change things at the service layer or below and the controller and view should not have to change at all.

Reply to this Comment

Not really, the idea would be to pass the form data into the service layer and let it do "whatever" with it. i.e.

<cfset pic = getPicService().getPicture(form.pictureID) />

This is to enforce encapsulation on the model. That way if you build a Flex UI it can also call the picService and pass in a picture ID. The service doesn't know or care where the picture ID comes from.

Reply to this Comment

@Brian,

What about properties? Like:

<cfset gallery= GalleryService().GetGallery( form.ID ) />
<cfset gallery.Set( "name", form.name ) />
<cfset gallery.Set( "description", form.description ) />

I figure you can't pass the FORM scope to the service, because how would the service layer know what to reference inside of the FORM scope?

Reply to this Comment

No you would indeed either pass the form scope directly or pass it as an argumentCollection. You definitely don't want to be doing anything like that in the controller. Further, if you already have the gallery ID you shouldn't need to manually set the name or description, that would be handled within the model by querying the database or the XML file. The service should be giving you back a fully populated, ready to use object.

Reply to this Comment

This is great stuff Ben, I think you just started a modern day Pet Market application! I am looking forward to the series and to sharing my input.

Reply to this Comment

Also remember that getters and setters are evil (http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html) and that ideally one would never call them at all. In practice one must usually call them when populating an object (unless one uses an ORM and the object is populated automatically by the factory) and when outputting object data for display (which is a strong argument for objects that can render themselves). I personally almost never actually all a getter or setter unless I have an object in a view and I am displaying its properties in a form or something. Every time you call a getter or setter you are drifting away from the real point of OOP, which is to *tell objects what to do*, not to *ask them for data about themselves*. If some code needs to ask an object for its internal data, that is a strong indicator that the logic should be moved into the object itself and the calling code should simply tell the object to do something with the data it already contains.

Reply to this Comment

@Brian,

So, following that train, you would pass in the form scope, which is already a struct anyway, for instance when feeding an update or insert method, but when instantiating an object you would send simply the ID as Ben noted and then let the object's method return what? Ideally, would it return a query or would it return itself with pre-filled properties?

I have always been leery of getters/setters in CF simply because of the overhead, but I haven't always known enough to know why I avoid them.

TIA

Reply to this Comment

@Brian,

That is a very interesting article. Just finished reading it. I will try to make some time to dig through the comments at the end of the article as I like the concept, but the implementation is a bit fuzzy for me.

Can you maybe just explain how you would move form data into a new object. For example, I am creating a new gallery that has name and description. We can load the object, which would be empty... and then what? Is there some sort of photo service which would then populate the object?

GalleryService.PopulateGallery( objGallery, FORM )

Hmmm, but then you would still have the same problem of loading the data into the gallery object... would it have its own load() method??

Reply to this Comment

If I were using my own factory and I was creating a new object (it has no ID yet), I would pass the form scope into the service. The service would pass that structure to a factory. The factory would create an instance of the specified object, and loop over the form properties and attempt to call corresponding setters on the object to set the object's state. The factory would return a populated object, which the service would then probably validate and persist (to XML or a database or whatever) using a Gateway object.

That said, having a load() or populate() method on the object itself is also a valid approach. The general idea is not to just pass a structure and then stuff that as instance data into the object, because that bypasses the setters which may contain additional logic beyond simply setting the property on the object. If one is all right with the tradeoff of ensuring that the incoming structure keys can be matched up with the names of setters on the object, it is usually possible to do all of this with a generic method call as I noted above.

I tend to follow the OO tenant to separate object construction from object use, which is why I would recommend using a factory. My approach of looping over the structure keys and calling corresponding setters is a shortcut, but it works in most cases. If the case arises where the structure keys do not match the names of the setters, this generic approach won't work and you'd need a more specific factory that knows how to take the structure and populate the object with it, possibly by calling setters directly rather than looping over the structure. Which is perfectly valid, it just means a bit more work.

Reply to this Comment

@Brian,
I thought I had all this stuff down, but the service/factory/decorator etc. stuff still throws me.

OK, so you would do something like:

form -> service layer -> factory <-> object

the factory then sets everything into the object from the form data that was passed via the service layer.

then, once everything's set/processed

factory -> gateway -> persistence layer

Reply to this Comment

@Brian,

Ok, so in the end, though, you still need setters / getters on the domain objects; it's just to have a bit more encapsulation as to how they are being used? I appreciate your explanation and thank you for your patience.

Reply to this Comment

This will be a cool thing to see how you and others implement this in their own way. If I have time to play around I may do this in MachII.

CoolJJ

Reply to this Comment

Yes, you still want getters and setters on your domain objects (and I greatly prefer explicit getters and setters over generic getters and setters, for API clarity). You just want to avoid using them as much as possible. Getters and setters *are* better than directly accessing instance data because you can change or add logic within the getter/setter and not affect the calling code. The real reason to avoid using them where possible is because dipping into an object and asking it for it's data is the top of the slippery slope to procedural programming. Again, where one has code that "needs" instance data from another object, in most cases it is an indication that the calling code should be part of the object being called. I try to keep behavior as close to the data it relies on as possible.

You're arrow diagram is almost what how I would approach it, with a few changes:

form -> service layer -> factory <-> object

after creation, the factory returns the object to the service. So in the service it looks like:

object = getFactory().getInstance(arguments);

Then, to persist, I do something like:

object.validate()
if (object.isValid()) {
getGateway().save(object);
}

or, if you prefer the active-record approach where objects can save themselves:

object.save()

and the object validates itself and saves itself. Which means the domain object would compose a validatorFactory and a gateway, so that it can use those objects to validate itself and save itself.

Make sense? I'm a bit worried that I'm ripping open a can of worms here and making something more complicated than you had intended it to be for your exploration.

Really, as long as you keep model-related logic within the service layer or "below" (meaning a gateway or factory or whatever), I will be able to take your app and modify the implementation within the model without having to worry about the controller or view. That way you can put up your take on things and then I (and anyone else for that matter) might take your version and modify the model, and then give it back for you to compare.

Reply to this Comment

@Brian,

True, we are getting ahead of ourselves. I'll jump on the interface portion (XHTML / Javascript), burn out those pages, and then, we can start to look at the domain model based on the interface.

At that point, we can start to think about actual architecture.

Reply to this Comment

Ouch - my brain hurts having read all the previous comments! I come from a background where the database defines the application so OOP is quite an alien way of thinking (and most databases are RDBMS not an ODBMS).
Good luck with the project Ben and thanks for sharing :)

Reply to this Comment

@Ben, Brian;

May I say I enjoy the exchange you two have going here. I hope the two of you keep this going to the end. This is interesting and educating at the same time.

Reply to this Comment

I stopped reading your blog posts. Almost all of your tutes involve some sort of sexual overtone, some of them are just flat out sexist and I got sick of explaining the racy pics to my female coworkers. "Look... it is tutorial on an advanced topic"... "yeah right".

Seriously. Not cool. And no I am not PC, but I do find a lot of it offending and demeaning to women.

Reply to this Comment

Well, the site *is* called Kinky Solutions. If something is NSFW, I usually just end up checking it out at home. I'm not sure why this specific blog post would warrant your response. This seems to be one of the tamer examples. Plus, I really don't find anything sexist in this blog, just someone who really appreciates the female form :)

Reply to this Comment

Don't bother responding to "Some CFer". He/She put in an invalid email address. I like that someone can come out and belittle the things I do in a public light and not stand behind their comments. I find this is the type of person whose opinion is of very little consequence anyway.

Reply to this Comment

@Brian, @Ben:

Using Brian's suggestion, assuming you pass the form scope to the service layer on save...

How do you initially cfparam the form values to begin with? Is cfparam not used? Is it better to do something like this:

try{

if (structKeyExists(FORM, "submit") {
/* save & validate form values */
GalleryService().saveForm(FORM);

} else if (structKeyExists(FORM, "cancel") {
/* Cancel Logic here */

} else {
/* populate the form scope with field values */
GalleryService().initForm(FORM);
}
}
catch(Any e) {
message = e.message;
}

....I think I'm still thinking way too procedurally. I imagine the controller would handle the error message and the cancel logic by using a different event for 'save' vs 'new'. If the save was valid, redirect to another event. If the save failed, reload the form. (The error message could optionally be handled by the standard view using ifDefined("errorMessage")

Would you also have a separate event for a new form vs. a saved form?

So... to cfparam or not to cfparam form vars?

Reply to this Comment

@Dan,

I am huge fan of CFParam. Once I get into the form-submit cycle, I like to keep all the data in the FORM scope. Of course, I am only learning better practices now, so I might be way off.

Reply to this Comment

@Dan,

Actually, the need for CFPARAM pretty much disappears if you're requesting and returning a bean to the view layer. In other words, if I open a form to edit a Person, then

personObj.displayFirstName()

will return "Jason", based on the ID passing to the DAO and filling the bean with the Person data, but if I open a form to create a new Person, then the bean will be empty, based on no ID or an invalid ID, and

personObj.displayFirstName()

will return "", which will leave my form field blank, as desired.

As for the action question, I normally do not make my Cancel links part of the form ... just link back to the View or the List or whatever. As for the Create vs Update action, I prefer to call a generic "save" action, and then let the Service determine whether it has a valid incoming ID (= update) or not (= insert).

Just my 2c

Reply to this Comment

@JFish,

With me, I think the beauty of CFParam comes in the "unsuccessful form submission," in which the submitted data needs to be re-displayed. I suppose if you have a bean that has no input validatoin (ie. strong typing), then this is fine. But, if you have a field that requires a date and a user inputs a non-date value, we need a way to echo that value back into the form. This is where I think the FORM scope is fanstastic.

Reply to this Comment

@Ben,

Aha ... I was thinking only in terms of populating the form before submission. You're right that CFPARAM is definitely useful while processing the form submission, especially since strong-typing non-required arguments in object methods can present problems: a blank is a string, not a number or a date, etc. Guess I just read the whole thing backwards, since I often see CFPARAM used at the top of forms to force-populate defaults and so forth.

Reply to this Comment

@JFish,

I think, however, that it is very much a procedural mentality in which I use it. To be honest, I am not sure how well or if at all that style of programming will play with OOP.

Reply to this Comment

@Jfish, @Ben,

I entirely agree with Ben: I always use cfparam as a good practice for form submissions - however, I don't know how to populate cfparams at the top of the page without breaking OOP concepts. Unless you can ask the object to create it's own params. I agree with jfish that it would be way easier to display a form if you allowed the object to populate the defaults.

GalleryService().paramForm();
GalleryService().displayForm();

The params could be included within a displayForm call, but you would need to have it separated for processing the form as you wouldn't always need to redisplay the form.

Reply to this Comment

@Dan,

The problem with that, I think, though, is that now you have the Service having to know things about the view (ie. what the form fields are named). I think this is also a violation of MVC.

Of course, in practicality, someone has to move data between the Model and the View. I had though that this would be the controller, but I think I am wrong according to what people have said above.

Plus, if it was the controller, you would not be able to handle different views easily. It's all still very confusing to me....

Reply to this Comment

@Ben:

But there's the problem. As the JavaWorld article illustrates, there will be crossover somewhere. Should the view know about the model or the model know how to render the model-relevant portion of the view? Maybe it would be best to have a service layer that builds the object relevant portion of the view depending on the target view (Flex, AJAX, HTML)

If I need to use OOP calls to output each little element of my view, am I getting any benefit of OOP or am I just complicating my procedural way of doing things?

Frankly, I'm not sure. I'm just playing devil's advocate a bit. This discussion is REALLY useful and timely to me. Thanks for putting it out there! :-)

Reply to this Comment

@Ben and Dan,

I don't know that it really impacts OOP in any particular way, right? I think it's just another tool in data validation. Just thinking out loud here, but you push it into the handler, not the form; the form should be simply the 'view' that results from certain events. I'm thinking of an object service, which might be called from your controller 'on save':

[cfset personObj = createObject("component", "cfcs.PersonService").init().save(form)]

Then the relevant method of the PersonService might have your CFPARAM calls, like so:

[cfcomponent displayname="PersonService"]

[cffunction name="save" access="public" returntype="cfcs.Person" output="no"]
[cfargument name="args" type="struct" required="yes"]

[cfset var person = createObject("component", "cfcs.Person").init()]
[cfset var personDAO = createObject("component", "cfcs.PersonDAO").init(request.dsn)]
[cfset var argStruct = arguments.args]
[cfset var rtnMsg = structNew()]

[cfset rtnMsg["success"] = false]

[!--- for args that are optional ---]
[cfparam name="argStruct.personID" default="0"]
[cfparam name="argStruct.email" default=""]
[cfparam name="argStruct.zipCode" default=""]
[!--- and/or for checkboxes, which don't even exist in form scope unless 'true' ---]
[cfparam name="argStruct.isAdmin" default="0"]

[cfset person.setPersonID(argStruct.personID)]
[cfset person.setFirstName(argStruct.firstName)]
[cfset person.setLastName(argStruct.lastName)]
[cfset person.setEmail(argStruct.email)]
[cfset person.setIsAdmin(argStruct.isAdmin)]

[!--- send the bean to the DAO to persist to database ---]
[cfset rtnMsg = personDAO.save(person)]

[cfif not rtnMsg.success]
[cfthrow type="PersonService.save"
message="Error saving person to the database."
detail="#rtnMsg.error#"
]
[/cfif]

[cfreturn person]
[/cffunction]

[/cfcomponent]

Then, in the controller, the resulting 'person' object, now populated, can be used in a review screen or used to repopulate the form, as necessary. Didn't really wire up the error handling, due to time constraints, but you could drive back to the review vs refill form in a number of different ways.

Reply to this Comment

@Ben:

...a further thought:

Am I correct in thinking that only HTML would need to call:
galleryService().displayForm()

Wouldn't Flex use some other method?

I imagine that the controller service could know what type (and source) of the view that was making the calls. Therefore the controller, based on it's own set of rules, could return the appropriately rendered form.

For example, if I had two pages that relied on the same service call, the controller could return different ones if needed. The view shouldn't be making that decision, should it? But it's still a lot for my procedural mind to embrace entirely.

Reply to this Comment

@Dan,

Just saw the display comments ... couldn't agree more. I know that Ray mentioned (above) having the object model handle display, but it makes me edgy.

@Ben,

You are right: the controller handles the communication between Model and View. Those 2 know nothing about each other.

But in any case, we still have the same problem you just outlined: either the View has to know which properties of the bean to call per field ("oh, I'm First Name, so I need person.getFirstName") or else the object in the Model has to know what the view expects ("person.display() will just fill in the [body] of my View for me").

Sticky situation. Any other insights into this from others?

Reply to this Comment

We're missing something crucial here and I think that it is because we are only thinking in a one-to-one world. What if we have a form that allows us to edit multiple items at one time...

For example, a "Top 5 Favorite Movies" list where we have 5 text input boxes , one for each movie.

In that case, we would need to translate each of those text boxes into a single Movie object, perhaps. Therefore, something needs to take that form and realize that that form is actually a composite of 5 different pieces of functionality.

Who makes that translation? The model can't do it because what if we need to update that from 5 movies to 10 movies? At least, I think it can't.

Maybe that's not the best example. But, my gut is telling me that we are missing something here. Like a code-behind for each interface. I wonder if John Farrar's COOP framework works this way; I vaguely remember there being code behinds for every display page. I think that is the piece that is missing - something that takes the infinite possibility of each display page and translates it into something that makes sense to the domain.

I have one very HUGE gut feeling about this, and that is - form field naming should NOT be required to fit a certain pattern.

I think this holds true if you look at FLEX. In FLEX, it doesn't matter what the text fields are called because it's really a AS3 class that pulls that together and formulates some sort of HTTP Service call.

That's what we are missing here... an AS3 class for our form views. Obviously not really an "AS3" class, but you get the point. We need something that translates the VIEW to the REQUEST.

Reply to this Comment

FWIW, several of the MVC frameworks provide a convenience methods that allows you to auto-populate beans from structs (form / URL scopes) by automatically calling a setter for each struct element.

That doesn't help with the multi-object edit page but it removes the need for most of what JFish is suggesting in the service layer.

Controller layer gets an empty bean (from the service layer), populates it from the user data (an event object in most MVC frameworks) and then uses the bean and the service to interact with, to get the operation done.

The difficult part in all of this is figuring out where to draw the line between the view-savvy controller and the model-savvy service layer if you are trying to create a service layer facade that could be reused for different front ends...

Reply to this Comment

@Ben,

One approach I have taken is to have Manager classes that act as proxies to the actual DAO/gateway stuff, and they are smart enough to figure out what needs to be done. The controller passes data from the view to the correct method, and then the Manager decides what needs to happen from there: maybe create 5 movie beans and pass each one separately into the .addMovie(movie) method or pass the form struct to an .addMultipleMovies(form) method. Using Model-Glue, for example, the framework takes care of passing the raw data, since everything gets loaded into an Event which is passed through all parts of the request, and modelglue.xml acts as the controller, announcing which methods get executed during the request, and then determining which view(s) to end up with depending on the method results that were called along the way.

Reply to this Comment

I have pretty much left cfparam behind because it isn't really necessary once you move to an OO approach. The idea being, say you have a User form. The controller will always get a User object and pass it on to the view for display in the form. You don't need cfparam at that point, the object will either have empty/default values for a new User, or the existing User's data, or the data that the user entered into the form but that failed validation within the User object prior to saving. The form doesn't care, all it knows is to call user.getFirstName(), it doesn't care whether that property is an empty string, or the existing user's name, or the value that the user just submitted.

Reply to this Comment

@Brian,

A wrinkle to that would be a confirmation checkbox. Something like:

[ X ] Click here to confirm that the information you provided above is accurate.

In that case, the checkbox is not actually a piece of information that is relative to the model data. It is simply a user-experience item that makes sure the user is conscious about their actions. It does not get persisted in any way - we are merely checking to see if it was checked as part of the form submission.

In that case, I would use something like:

<cfparam name="FORM.confirm" type="boolean" default="1" />

Reply to this Comment

Ben, I'm not sure I follow that. You're saying there is a "confirm" checkbox that actually doesn't do anything or isn't used in the model? Most systems I've dealt with that have, say, a "I have read the terms and conditions" checkbox is persisted in the model. I just can't think of a reason one would want a checkbox that actually was meaningless...if anything, wouldn't that be *against* usability rather than helpful?

Reply to this Comment

@Brian,

Because a confirmation checkbox has no meaning when it is persisted. If you cannot create a record without "agreeing" to the terms of use or whatever is being presented, then the very existence of the record implicitly states that the checkbox was checked when the form was submitted; it's persistence would be redundant.

Therefore, you would have an element that had meaning but whose persistence is not necessary.

Another scenario would be a "confirm your email" input where you have to enter your email twice. The second email is not actually part of the model - it is a device used only to help ensure accurate data. The needs to validated against the other email box, but not from a model standpoint - only a user experience stand point.

Does that make more sense?

Reply to this Comment

Ben, it is great seeing you tackle this again. I hope you are more successful this time. I will definitely keep tabs here and chime in with any comments. This already has me thinking a lot about how I'm approaching things. A great read! Keep it up!

Reply to this Comment

I suppose that in my opinion, if a checkbox like "I read the terms and conditions" is a business rule that must be enforced, then I wouldn't call its persistence redundant. But the issue isn't really whether that is saved to the database or not.

I wouldn't say that having fields like "confirmedPassword" or "confirmedEmail" in a User object is somehow a problem. Even if those properties aren't actually persisted, the whole point of the object is represent a useful abstraction. There's no rule that says every piece of information in an object has to end up in a database. If your use case requires an isConfirmed() method, or a confirmedPassword property, then by all means, use it. Assuming that one has a validate() method that would detect things like "passwords don't match", the object *has* to have those properties anyway, for the validation to be worth anything.

Reply to this Comment

Let me respectfully disagree with my friend, Brian. It's not that what he's saying is wrong; it's that it's not terribly helpful to someone learning OO. It's premature.

I'd suggest you forgot about Transfer, service layers, factories, and AOP -- for now. You'll have time to integrate all that (minus Transfer, of course!) later but for now, the danger is that you'll be overwhelmed and not learn OO at all.

One eats an elephant a bite at a time, or so experienced elephant-eaters tell me.

Reply to this Comment

I definitely agree Hal. A while back up in the comments I had said:

"I'm a bit worried that I'm ripping open a can of worms here and making something more complicated than you had intended it to be for your exploration."

So what you're thinking is the same as what I was worrying about. I agree that ORM shouldn't be a concern (I'm pretty sure I didn't mention Transfer but someone may have, its been a long thread).

I do think the advice to keep a stable boundary around the model by means of a Service layer would be worth attempting, but everything else can come later, as Hal points out. The only reason I say keep the Service layer is because it makes other refactorings so much easier because the changes are mostly limited to the Model. Which means you can try out all sorts of crazy ideas without making changes to your Controller or View.

Reply to this Comment

Ahh I did mention that I might add Transfer to a version of his Photo app after he puts it out there, just to show how it might work! Blogs aren't great for long discussions heh.

Reply to this Comment

Back from a long weekend. Still trying to consume all of what has been said here as well as in the other post on the Flex and MVC diagram. I have finished the prototype and will be posting that shortly.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.