By Peter Bell

Is Everything a Collection?

If a collection is a set of 0..n records, is it really worth hard coding the special case where n=1?

Help/Input requested!!!

Because I like writing a small amount of code that solves a large number of problems, I really don't like coding special cases unless it is necessary. For instance, many people write scaffolding forms that allow you to edit a single object using all of its fields. But what happens if you just want to change the price and title of ten products using a single form? Of course, you could hand code that, or you could just pass a property name list to your form (the fields to include in this instance) and automatically make all of your forms support editing of a collection of n-records (including the special and most common case - where n=1).

This issue has come up as I try to decide how to implement my business objects. I need to have a base business object with generic getters and setters, and I need a collection object (I used to call it an Iterating Business Object) with my updated iterating methods - first(), next() and hasMore(). . . .

So, how do we implement this? Well, we could have (say) a Product object that extends BaseBusinessObject, but then if I want to return more than 1 product, I'm going to have to decorate the Product object with a Collection object and create a bunch of delegates to replicate all of the methods in the Product object. This works, but increases the complexity of the creation code (now my service methods or LigthWire needs to be really knowledgeable about how to decorate a business object with an iterator) and it adds extra code and breaks DRY because the collection needs to know and replicate the methods of the business object.

Of course, we could be smart and use introspection of the business object (for each method in businessobject create delegate) to dynamically generate the necessary delegates automatically at runtime so we wouldn't have the additional code, but that might confuse the unwary and because there is no way to define a function in ColdFusion without writing it to the hard drive, it'd be a bit of a hack.

I'm tempted instead to have Product extend BaseCollection (which extends BaseBusinessObject) which means I just need to create Product object and don't have to worry about decoration or delegation.

Aha, you say but that is evil for it fails the "IS A" test. You are right. If you think of a Product as a business object, it is clearly *not* a collection and so it would be evil to extend BaseCollection - composition as outlined above (specifically decoration with the iterator being composed of the business object) would be the right way to go.

However, if you think of a Product as a collection of n-products where n is sometimes 1 then it is perfectly appropriate for ProductCollection to extend BaseCollection. This also gives a bunch of benefits in terms of code reuse as I can then use the same code to display and process 0..n records as to display 1 (while still being able to use different code when necessary).

The more I think about this approach, the less I like it. I really don't want to go around calling my business objects UserCollection and ProductCollection and it arguably violates the Single Responsibility Principle of doing one thing well (although it depends on whether you argue that a Product is a product or a collection of products).

For now I'm just going to make Product and User extend BaseCollection, but I'll refactor as soon as I get a better idea for how to implementing this without creating more problems than I solve.

Any ideas/input wildly appreciated!

[UPDATE] At the moment, I've created a BaseGetSetObject with generic getters and setters used by both business objects and things like the Input object. That is extended by BaseBusinessObject which has iterating methods (first(), next() and hasMore()). Then the business objects (Product, user, etc.) extend that. While this seems a little hacky at first, I really do like the fact that I can rely on the iterator methods so if I want to write display or processing code it that supports 0..n objects (including the special case where n=1) I can do so. It takes a little mental step to think of business objects as fundamentally being collections of 0..n objects, but when you do so it really does simplify the code base in a number of places.

Will be interesting to see feedback as I'd characterize this as one of the "most easily criticizable" decisions, but when I get off my architectural high horse, it just seems to flow and work nicely within the code. Looking forward to anyone elses thoughts!

Comments
"However, if you think of a Product as a collection of n-products where n is sometimes 1 "

But its not a collection of n-products. It's a product! =) I think you're breaking the abstraction here, and I don't think that's normally a good thing.

What I'd like to know more about (because I don't think it was clear) is what trouble the "normal" way is causing. It seems just as easy to provide something for collections like:

while (hasNext)
performOp(thisOne)
end

and then performOp is the single case.

This way, you aren't breaking abstraction, and anyone who needs to maintain the code doesn't have to say "why is he looping over a product?"

Of course, the simplicity of my viewpoint here, I think might change if there is something I don't understand about the trouble the normal way was causing. I suspect that may be the case.
# Posted By Sam | 2/20/07 11:36 AM
I guess I'm asking for a shift of view and saying that in so many cases, we're better off solving the general 0..n problem that don't know that it is worth solving the single problem.

Ever written a form to edit a product and then been asked for multiple select to edit multiple products using a single screen (and perhaps a subset of fields)?

Ever written code to add a product to the cart and then been asked for a multiple add screen to allow people to add multiple products with a single click?

Ever been asked to write a method to save a user and then been asked if you could pass in a collection of users to be saved?

The point is that because I'm writing generalized code, I've been bitten by this so many times in the past I'm wondering if it is ever worth leaving out the iterator or whether it makes more sense just to write everything to support n-records.

Also, supporting collections sometimes takes more than just a while loop around the single - if you do that on form fields you end up with six form.FirstName's overwriting each other - same for controller logic for a collection - it required something like form.FirstName#InstanceCount# for all of that to work.

Still not convinced, so would love to hear more counter arguments/questions!!!
# Posted By Peter Bell | 2/20/07 11:50 AM
Of course, I've been asked for those. =) But, I'm not saying you shouldn't support it, I'm just saying you shouldn't break the abstraction by saying that a product is a collection. at least on the model side, it is trivial to do both.

And if you are implementing the view and the controller which would presumably create the collection from the form and pass it to the model, then keeping abstraction is still trivial. Once you have solved the case for n, as you say, it is also trivial for n=1.

But I think it is just slightly harder than trivial to provide the correct abstraction, and allow for both cases. There isn't that much extra code to do both while keeping the abstraction straight for future eyes (including your own).
# Posted By Sam | 2/20/07 12:12 PM
Yep. That's the counter argument. Still not sure so for now going to keep on down the dark path and see how it looks before I refactor to what is *probably* the right approach. I just *hate* writing extra code!
# Posted By Peter Bell | 2/20/07 12:28 PM
I know. I'm the same way. Guess that's at least partly the reason you're writing the thing, right?
# Posted By Sam | 2/20/07 12:38 PM
Yep. I want to be able to be more productive than anything out there (including Django which to me is the closest to what I want), so I'm just trying different stuff and seeing what sticks. Pretty happy with progress so far!
# Posted By Peter Bell | 2/20/07 1:04 PM
You are not alone in your feeling that "1" is a special case. This is even an open field esp. in knowledge representation and linguistics.

Look e.g. at Mereology, an alternative to set theory where there is no distinction of {apple} and apple. See John Sowas nice short summary in his mail: http://www-ksl.stanford.edu/email-archives/interli...

or just read this excerpt:

5. As another example, mereology makes no distinction between an
individual and a singleton set nor between different ways of
building up sets by levels of nesting: {a,b,c} would be identical
to {a, {{{b}}, {c}}}. Goodman summarized that point in his slogan
"No distinction of individuals without distinction of content."

btw. there are other niceties:

4. As a typical example, Nelson Goodman cited the political subdivision
of France into departments or provinces. When considered as sets,
the set of departments of France is very different from the set of
provinces of France. But in mereology, they would both be considered
identical -- the total territory of France.
# Posted By Mathias Picker | 7/28/07 6:18 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.