Saturday, September 17, 2011

Can you ever really have two loves?

Listened to the #cfdevweek recording on the ORM/Hibernate in CF9 presentation by Bob Silverberg today. I have a lot of questions/reservations about how the interaction with the database is implemented.

Fundamentally, I guess it comes down to whether or not we ought to be writing ad-hoc queries or using stored procedures... As long as I have been a CF developer, I have had to talk to a database (12 years and running). In that time, I have slowly and painfully grown adept at optimizing SQL. Our shop is data heavy and it is not just my data, but data shared among many interconnected and complex systems and organizations. In my 3 years employ, I am a full convert to stored procedures for efficiency (reduce trips to the database server, one of the costliest transactions to a web request; stored procedures are compiled and execute quicker than ad-hoc queries in general), security (tight data typing, ability to lock down permissions), separation of concerns (Coldfusion should contain web server code, the database contains the SQL code), encapsulation, etc...

Anyway, I may not be making my case very articulately, but I am worried that there is now going to be pressure from the community to use ad-hoc queries. Hopefully, ORM will work with sprocs as well. Guess I should have asked that...

Wednesday, September 7, 2011

Refactoring Objects, or the Woes of the OOP Impaired Through a Series of Mixed Metaphors

I'd like to enter an official objection to the OOP review board against the principle that objects be open to extension and closed for modification.

This may be great for Object-Oriented Gurus who feel they have mastered "architecting" OOP. For those of us learning as we go, who are still under pressure to produce production-ready products, and want to leverage what we know, but what we know may contain glaring holes, the process is a lot more grueling: refactor, refactor, refactor seems to be the rule.

Every time, if you're lucky enough, you learn something new, and hopefully do not repeat the same mistakes next time, but no amount of pedagogy is going to help you predict problems and proactively avoid them--at least not me: I have to hit my head and fall in the hole and turn over the can of gas near the open flame an estimated 300 times before I learn my lesson. That's anecdotal, of course, but 300 times seems like a really good ballpark. But I guess that's a testament to learning, after all--it is not a very clean process, you have to get your hands dirty and make some really awful mistakes; otherwise, you end being too precious to take down from the shelf...

Maybe we can argue that I just don't have the OOP gene, or that my OOP gene is somehow deficient, and I should leave the cooking to those who know better, but until I find a new shiny object to steal my attention, my days are a comic tragedy of ponder, ponder, ponder, refactor, refactor, refactor, implement, implement, implement, run up against catastrophic design flaws, rinse, repeat.

Monday, September 5, 2011

Iterating Business Objects

I loved the idea of the iterating business object when I first encountered it. I'm hung up right now on this scenario:
product1 = Products.GetProductByID(1);
product2 = Products.GetProductByID(2);

WriteOutout(product1.Name);
WriteOutout(product2.Name);

WriteOutout(product1.Description);
WriteOutout(product2.Description);

So I assume here that Products is a service that sets the index and returns the IBO object. Unless I'm mistaken all 4 of these outputs would return the name and description for product2.

I think this is a real-world example: say I have a collection of 10 products, and I want to compare only 2 of them... There are ways to code around this, once you know it will happen, but my biggest concern is that having Products.GetProductByID(1) return the IBO, which is just a shell that defines a record, until it has an index, is misleading, as we would naturally assume it to always return the data for product1 when in fact it just returns the data for the current product.

Something like this is less natural, but I'm considering whether it isn't more "self-evident" what is going to happen:
product1 = products.getProductIndexByID(1);
product2 = products.getProductIndexByID(2);

WriteOutput(Products.GetProductProperty(product1, "name"));
WriteOutput(Products.GetProductProperty(product2, "name"));

WriteOutput(Products.GetProductProperty(product1, "description"));
WriteOutput(Products.GetProductProperty(product2, "description"));

Obviously, this is just an example that could be further abstracted to handle any collection, not just Products.


There is a second reason that I like this, as clunky as it seems. I can "late-bind" properties that I might or might not need, to avoid storing too much data up-front; so, when GetProductProperty saw that "description" was not yet populated, it could look at a map that tells it how to get description.

I'm toying the notion of the following Objects:
- ProductMap - tells me which properties will be available in this IBO, and which methods are responsible for getting those properties.

- Collection - the "transformed" data, stored as a query or structure...

- Iterator - provides the ability to move/loop through a collection

- Service - methods to locate the index of a record, as well as methods to populate and retrieve properties on the record. It transforms the data as it gets it, using instruction from the ProductMap.

Thursday, September 1, 2011

Coldfusion Enum "Data Types"

I really wish there was some way to create a "virtual object" in Coldfusion, or else to specify more than object paths as custom type in arguments...

My coworker was talking about enums a few weeks ago, and I blew him off, but then I cracked the Gang of Four book, and it is so dense I started to try to code the samples so I could understand things better. And right away they start with an enum:
enum Direction{North, South, East, West}

And so out of sheer ADD, I started puzzling over how to do something like this in Coldfusion. Basically, to be able to say this argument wants an integer limited to the following values, and those values will mean this. Obviously, merely using the generic "numeric" type and then validating inside the function is neither self-documenting nor reusable.

Best I've come up with so far is this:
Enums = CreateObject("component", "packages.utils.enums.EnumLibrary").Init();

Direction = Enums.Enum("packages.utils.enums.Direction");
Room = CreateObject("component", "packages.maze.Room").Init();

Permitted values for Direction: #Direction.PermittedValues#.

Room.SetSide(Direction.North, "Wall");
Room.SetSide(Direction.East, "Door");
Room.SetSide(Direction.South, "Wall");
Room.SetSide(Direction.West, "Door");

Side #Direction.North.Index# - #Direction.North.Value# #Room.GetSide(Direction.North)#;

Room.SetSide is defined:
function SetSide(packages.utils.enums.Direction, String);

Unfortunately, this requires a component for every single enum (packages.utils.enums.Direction). It also requires that the type/name for each enum item (North, South, East, West) is that cfc, which means instantiating 4 objects, as far as I can figure (reusing the object creates lots of reference problems).

When originally creating the packages.utils.enums.Direction cfc, you just have to provide a list (THIS.Definition = "North,East,South,West"). EnumLibrary::Enum transforms each of those list items into members containing the enum object (enum.North = CreateObject("component", "packages.utils.enums.Direction").Init()).

Obviously, the enums library can be stored in server scope. And the enums don't have to be created before they are needed, but will then persist in the library as well. But that could mean a lot of objects sitting in memory.

And for the week of work and effort exploring various dead-ends until arriving at this solution, I still can't think of a circumstance where my life would have been so much easier for having enums...