When to Make your Own Types - a Nice Introduction
Unfortunately, implementing these in ColdFusion is a bit of a pain only due to the cost of object instantiation, so my best solution to date is to "hack it" using a DataTypeFacade singleton and getting my business object to delegate most property specific actions to that facade which in turn calls the appropriate data type (so I have singletons for DateTime, SSN, PhoneNumber, etc.). That works pretty well in practice.
How do other people implement custom data types in their CF applications?
[Why a facade? Because I don't want to have to inject 50 different data types into each transient business object - I feel it is cleaner to just inject a single facade - especially while my API is still settling down.]





I haven't really thought about why I don't do it in CF as I might in Java, but this might go back to my post some time ago at http://www.codeodor.com/index.cfm/2007/1/15/More-c...
I was speaking generally there about creating more classes in other languages (like Java) than I do in CF, and speculated it was because 1) I've developed bad habits in CF, 2) the performance issues in the back of my mind, and 3) CF is just more expressive and has less need.
The part I didn't think about were the small "types" that you might just want for documentation and encapsulation purposes. I realize that's what a class is too, but as long as we are making a distinction, I'll settle on "type" to mean things (I assume) most people wouldn't think to create a class for, like geographic coordinates, or age, or perhaps x,y point on a plane if you weren't creating a graph/geometric library (of course, if the focus of your application included any of these, you WOULD probably think to create a class for them, but if they were only there incidentally, you probably wouldn't).
So with that in mind, I'm just going to throw this out there, and see if it sticks. It's not something I've given a ton of thought to, so forgive me if its heretical (I think it is, but since I try not to be religious about code/technology related things, I have no problem throwing it out there)
I wonder if its as beneficial in dynamic languages as static ones (or even in static ones with the tooling we generally have available). Certainly we get a good degree of documentation by simply choosing good variable names (this is not limited to dynamic languages of course). On top of that, if you need to change the representation (say from int to string or float), you don't have to go all throughout the application changing the type everywhere (in fact, with refactoring support nowadays, this isn't even all that bad in static languages anymore).
So, two of the big reasons for going ahead and creating the type aren't really issues. The only big issue I immediately see remaining is something like, as Fowler mentioned, needing to store not just an integer age, but the number of months as well. Of course, this is doable by using floats, but should you actually want to store it as two integers (one for numYears and one for numMonths), then you run into a problem.
But for me, I think YAGNI is in order. How often does that happen? Enough to justify doing it for every conceivable type? I haven't been burned (that I've noticed, anyway) by not doing that, so until I see otherwise, I'll probably stick to not creating the types. When I see that I'm doing a calculation in 2 different places, and that it is not localized to one class, then I'll go the full abstraction route and create a type/class for the offending code. Even then, I may just choose to use a mixin, but maybe not.
I certainly see the value in doing as Fowler suggests, but I also see value in YAGNI (and I think I see more value in YAGNI). You just REALLY have to follow it - my policy is that the first sign of duplication that is not localizable to one class probably means you ARE going to need it.
I actually implement tis using a datatype singleton and a set of singletons - one per custom data type, but the power and DRYness of this just rock. It is how I get something like Djangos custom data types and I couldn't imagine writing web apps without this now - even if it breaks MVC (or extends it by suggesting there are four core things - Model, View, Controller and Datatype).
Out of curiosity, what would be a /standard/ way? (I ask, because I thought that /was/ the standard way =) )
As for the rest, I see your point, but I've gone a different route. It seems like the purist in me would say, each individual property of an object should be an object, and the view (for instance) should Tell (Don't Ask) it to draw itself. But, (and correct me if I'm wrong), you've got (what seems to be) a data type datatype (if that makes sense), where you might call it a service, telling it to draw_a_field_for(the_value_of_some_property_that_is_the_type_you_represent). In essence, a Type object (or object of type Class), as opposed to each one being an object.
Where I've differed is that I've got a query object which holds all of the properties and associated metadata and data for a given class. (Say, each class would have columns: field_name, field_type, display_order, etc. Then the view will loop through that query (sorted by display_order) and will invoke methods like display_#field_type#_field(value).
I get the benefits of reuse, but perhaps not the benefits of smaller files. I haven't run into a problem doing this (yet?), but I like (my understanding of) your approach as well (enough to try it at some point).
Thinking about it, I feel like I'm using my view as a service class, but I'm violating TDA it seems. (I'm a bit fuzzy on what exactly is telling and what is asking - because some ways seem like either word works).
On the other hand, my understanding of your approach means yours is also violating TDA, in that you are not telling a property to draw itself, but telling its type to draw a value (and I admit my understanding may be completely wrong). But your approach has the added benefit of higher cohesion, smaller files (and thus more easily read code), among other things related to the two.
I don't know that I'd go as far as adding the D to MVC (though, I can't be sure if it may actually warrant it), but I /can/ see value in the approach. I just am not sure if it brings /enough/ of it (and only because I've accomplished the same without the extra classes, at the expense of a little more complex classes (versus a more complex architecture, I suppose).
I'm going to give it a try and see if I like it better (though I think this approach is somewhat different from what Fowler described in the article you linked). I'm not sure where yet, as I've generally been trying to avoid using the session or application scopes as requirements (I've broken down on session only because there seemed to be no good way to pass a message that I needed to pass except through the URL).
Thanks for the clarification, and response to anything I've said here that needs to be set straight =)
Well, what makes my approach non-standard is that I violate MVC. I see Data type as a fourth primitive as I find there is such a strong linkage between how I display a field (View) and process it (Controller) that it makes more sense for me to put it all into a single bean.
In THEORY I have exactly what you are suggesting. In theory every single property is its own object. In practice, if I'm displaying a list of 200 objects each with 10 properties, I'd have to create 2000 transient objects just to display that view. In ColdFusion that would be non-performant, so I use the whole DataType facade and Singleton data types to spoof what I really want in a more performant manner. I still call User.displayField("FirstName"), but instead of delegating to a FirstName object, it delegates to the DataType Singleton, passing in the property name, property type and vale. A hack, but it works quite nicely and is well encapsulated within the Base beans displayField(), displayValue() and other methods so fairly easy to reengineer if ColdFusion ever becomes Ruby :->
Your approach is similar except you have everything in one big file. I prefer to have each of my custom data types (and for me an application could have 50 or 80 different custom data types) as a nice tight bean - performance is still fine as they are all singletons, but your approach was what I did last time round (actually I used a case statement instead of lots of discrete methods which was why I refactored).
I really don't feel I'm violating TDA (Tell - Don't Ask). I'm just hacking round the fact that creating a transient object for every property of every object I display would be horribly non-performant in CF. When I write the Ruby version of the framework, I'll be able to get away without that hack.
This approach is different to Fowlers insomuch as his approach is specifically part of the model handling model concerns. I happen to find that for web apps I get a more maintainable code base adding D to MVC after trying a lot of other alternatives, but your mileage may well vary!
I see, but I don't quite understand what violates MVC about the approach. Is there something in that linkage? I never thought MVC meant we can only have classes that are models, views, and controllers, but I may have misunderstood MVC (or what you are saying).
"Your approach is similar except you have everything in one big file... "
... Which I dislike very much, and makes me want to try what you are suggesting =)
"but your approach was what I did last time round (actually I used a case statement instead of lots of discrete methods which was why I refactored)."
One thing I thought about doing is attaching methods to queries. But, that can't be done (unless they let you add a pointer datatype in cf8!). What would be more accurate and possible would be convert to array of structs and attach the method to each struct (or just one if it made sense to do it that way). But, that approach seems overly complicated even if it would be pleasing to use (which I'm not even convinced of yet).
In the end, it doesn't bother me THAT much to have the big file if its not overly complicated and if it doesn't need to change much (since the idea is to never have to look into it anyway). At this point I feel it is meeting those conditions, but that doesn't mean I shouldn't improve the architecture if I have the time and desire.
"I really don't feel I'm violating TDA (Tell - Don't Ask)."
I see what you mean now with the extra explanation.
"When I write the Ruby version of the framework, I'll be able to get away without that hack."
I have a feeling with just about any ORM you'll have problems with a large enough data set (of course) =). But I think you'll have problems much sooner (or with a much smaller data set) than you would have them /without/ the ORM, and I would guess it's not linear degradation, though I have no proof of that guess. Of course, with CF8 if they've truly increased the performance of creating CFCs as much as I've read, it may cease to be a problem.