Advice On Balancing Cohesion, Packaging, And God Objects?
I have started to put some more work into my POI Utility ColdFusion component, which is exciting, but at the same time, I am afraid it's getting too big. Right now, the POIUtility.cfc ships as a single ColdFusion component, but it is already a very big file. If I start to add more functionality to the same CFC file, it is going to get even bigger and, at almost 2,000 lines of code already, this just doesn't feel like the right strategy. My gut is telling me to break it up into multiple files and make each of those files more cohesive around a single task rather than this general task of reading and writing Excel files.
My gut tells me to avoid creating a God object that can do everything Excel-related under the sun. But then, on the other hand, this is a third-party "product," and I want it to be as easy to install and use as possible. The more files that need to be installed, the higher the complexity of the project and the more likely something will go wrong once it is out of my hands.
Does anyone have any ideas or guidelines that they follow when trying to balance Good programming techniques with ease of packaging and distribution?
If you wrote all of your cfargument tags on a single line, you'd probably save 400 lines. :)
Seriously though, you could possibly break what you have into separate CFCs:
these would be loaded into or by POIUtility.cfc (or POIService.cfc).
Then the user would only ever call the service object.
All arguments on one line?!?!? What kind of craziness is that :P
Yeah, I like the idea of having a main POI object that loads composed objects to delegate work to. Thanks for the feedback.
If you're looking to model it after something, check the cFlicker app. I thought it was a fairly organized way of extending components.
Anyone else have code sample they'd like to show?
Thanks for passing that along. I will definitely take a look at it.
Yeah if you can you want to cut down and break up your object into multiple ones. This helps organize but also keeping your objects focusing on a single task rather than several.
Even if its a third party product I definitely recommend looking into breaking it apart. Many API's out there will do that and I think it will help you and other developers out a lot more. The cflickr is a nice example.
First poster was right -- it would not seem like an overwhelming CFC if you removed most of the extraneous spacing. It is my only complaint about using your CFC as is -- I always end up removing all that extra space to get the filesize down and (to me) it is easier to read and follow without it. Maybe offer a "compressed" version of it?
I'll agree with these other guys, and I sympathise with you, I've been in this same situation before, and generaly once you start adding a few other design patterns like DAO and Gateway it can become more and more difficult to package your concept neatly for distribution.
When I find I have a task which I break in to logical chunks in different CFC's I use a Service which encapsulates all the popular tasks and handles the communication between the app and these different cfc's.
A great example of this would be a user authentication system, where you have lots of things to handle, from profiles to credentials and entitlements, user tracking, each of which has its own set of persistance objects and beans, so I implement a SecurityService.cfc which has nice simple functions, such as changePassword() and getAuthenticatedUser() which does all the heavy work, the service then becomes my API for the app, and all of ther other more functional CFC's only ever get called by the Service.
This way the module can be dropped in to any application and it only takes a couple of lines of code to implement as we're dealing with a single service file, which has a clean and well documented API.
It takes a bit of getting used too, but I've found that when it comes to maintanance and things, this kind of pattern is very neat.
Just to play devils advocate, Alagad's Image.cfc is frikin huge, and that sucker works great, fast, and is super easy to install. =) Alagad's weighs in at 4000+ lines, and he puts arguments on the same line!
I haven't had a lot of time to read the flow of data in the CFC. But the first comment does seem like it could help you out for a first step. On the last three functions you have 6 arguments. You could reduce this to one argument if you create a bean with those attributes. Peter Farrell made a really awesome bean generator to make it easy: http://rooibos.maestropublishing.com/
After you create the bean, you can just set all the information in the bean and pass the bean around instead of passing all the arguments individually. I tend to make the beans as dumb as possible. If I need additional functions to do any processing on the data I'll extend the bean. This way, if I add more fields to the bean, I can just regenerate it and not worry about merging any code.
That aside, I also use the service method of breaking things down. In your case you could probably create two utilities accessed through a POI service. Maybe do a style utility to handle all your css, and an Excel IO utility to handle all the reading/writing. Then just tie them together with your service.
What's wrong with multiple files? It's just as easy to drop a directory into my application as it is to drop a file in.
And my application only needs to know about one CFC, the PHB (aka, facade, manager, wrapper, service ...). My app calls functions in PHB, and the PHB delegates all the work to small, specialized CFCs. You could add 100 CFCs behind the scenes; it doesn't matter to me, because I'm only talking to PHB.
Dont mis-understand me, I'm not saying a single file is the *best* way to do it, but it would obviously work. If you can drop a single file into a custom tags library instead of mapping a location, that is simpler to me. Also, it will become 1 object creation instead of possibly several internal objects. Not to mention the fact that just because you *can* abstract and complicate something, doesn't necessarily mean you have too =)
'abstract and complicate'
Surely the purpose of abstraction in this instance is to uncomplicate the current solution?
Breaking a large component in to logical pieces makes life far simpler than scrolling through lines and lines of mix and match code. Especialy as a large amount of that code doesnt actualy concern the user as they're private or calculative functions which are not called directly by the application.
By abstracting all the worker functions into thier own logical layer and only presenting the user with a nice friendly service API you are making life a million times simpler for them.
You needant even supply them with all the files, you could make the service API a wrapper for web service calls and host all the other components on your own server if required, then they would have only a single file to implement, and yet you can keep everything organised!
Abstraction can be taken too far I know, but in this case i think its appropriate.
Hey, don't look at me. Your own gut is telling you to break it up into smaller files. :) All I'm saying is if you do go that route, it's not going be any more difficult to install and use.
You don't have to worry about mappings, as long as you write createObject("component", "ExcelReader") instead of createObject("com.kinkysolutions.poi.ExcelReader").
Oops, ignore that last comment. I was replying to a comment by Justice, thinking Ben had written it.
But I second Rob's comment, beginning "'abstract and complicate'." He's right on.
Gimmeh your new POI and quick!
Advantage of a simple service cfc with one line arguments that calls the big ol CFC - Less documentation!
Still working on it :)