Whenever a class in my model contains a collection which requires that particular care be taken with its items, there’s an internal debate regarding how to expose it to other classes. And with this, there are two major schools: one, the paranoia-based approach which doesn’t allow external code to touch the collection’s internal items and two, the trusting approach which just returns the collection for everyone to deal with.
The paranoia approach
The paranoia approach retains the collection modifications inside the class. Want to add a value? Sure, but through an addItem method. Remove a value? Go ahead: removeItem. Get the collection itself? No problem at all, getItems will return the result of the Collections.unmodifiableCollection method. You get the picture. The benefits are simple: the class controls the collection. Throwing an exception when an item is added which is not logically correct for the collection to contain or firing an event when an item is removed can be easily programmed when the methods are implemented within the class. And no need to worry about pesky wrong code creating bugs by clearing the collection “accidentally” – yes, we know it wasn’t an accident, and you know who you are!
The trusting approach
The trusting approach assumes that the user of the class knows what they’re doing. In effect, there are two methods: getItems implemented as return items and setItems implemented as this.items = items. As simple as that. This can be great when you want your code to be simple, but more importantly when you want to use dependency injection or object-relational mapping frameworks. For example, currently Hibernate behaves strangely when the collection returned from a getter is different than the one Hibernate set originally using the setter.
Pros and Cons
I guess it’s time for a comparison table:
| Use | Paranoia approach | Trust approach |
|---|---|---|
| Maintenance | A lot of boiler-plate code really makes this difficult to maintain | Simple code |
| Exporting the class as part of an API | Since the class is foolproof, there’s no fear of an accidental change of the collection by a third party developer | Not recommended unless contents of collections can be changed without causing major bugs |
| Using as a part of an internal model representation | Difficult to map to an ORM or DI solution | Easy to use as this is a classic POJO implementation |
What are your thoughts on the matter? What do you use, when and why?
May 19th, 2008 at 8:38 pm
I usually wrestle with this when designing classes. Recently, I’ve been tending toward the paranoia approach. It seems to me that once you expose the underlying collection developers will find many creative ways to do things that you never intended. By controlling access you can also provide methods that make sense to the collection at hand (since it isn’t generic).
Also, in some cases I find that just returning a ‘read only’ iterator works too.
May 19th, 2008 at 9:37 pm
@Paul: How do you deal with connecting your code to frameworks like Hibernate? Or are these classes that do not need to be managed by an external framework?
May 20th, 2008 at 12:40 am
Sorry if this is a duplication, it had an error last time I commented.
The “Trusted” approach is also breaking encapsulation. What happens when you change the implementation to use a different collection implementation, or put additional constraints on what can go into the collection (or where). Even if the client doesn’t break your code by manipulating the collection, you can break client code by changing the way you manipulate it.
Part of the “Paranoid” approach is also called “Defensive Programming”, but it is also “Encapsulation”. Rather, Encapsulation comes as a side-effect
The point is that you should avoid relying on your client to maintain your own invariants.
As for dependency injection, you can have a “setCollection” method which actually copies the items into your own collection, verifying your invariants as (or before) you go.
May 20th, 2008 at 2:06 am
Hello
Firstly, to be factual about it, the trusting approach violates the “law of demeter” object orientated design guideline:
http://en.wikipedia.org/wiki/Law_Of_Demeter
Personally I find that the paranoia approach much better. I use it with hibernate, using field access (simply set access=”field” on the mapping relation if you’re using .hbm.xml files, otherwise hibernate annotations and JPA default to field access). What’s really great about it is that I can add an item to the collection unsuring it is valid and not a duplicate, and tell hibernate to save the association in a transaction, and anywhere I want to do that it’s just a matter of something like:
parent.addChild(“Bob”, Gender.Male);
rather than (at the very least):
parent.getChildren().add(new Child(parent, “Bob”, Gender.Male));
dao.update(parent);
If you use the trusting approach the code can very quickly become difficult to maintain, especially if there is suddenly a new condition you need to enforce. I find the overhead of having a addChild, getChild, removeChild and getChildren to be very minimal.
Anyways, just my thoughts
May 20th, 2008 at 5:39 am
Okay, to be completely honest, I prefer the paranoia approach. I used it in an application of mine and then it broke to pieces when I tried to bind it to a database using Hibernate; admittedly I’m no Hibernate expert so I probably didn’t know something that had to be known.
@Daniel: Having a “setItems” which calls “addItem” in a loop was my first choice of action. However, if you’ll notice the link I have on the post itself to a Hibernate FAQ site, this causes problems and Hibernate acts weird, adding and removing items unnecessarily.
@James: I didn’t try the access=field method. However, I just assumed it wouldn’t work because it uses reflection and the collection member is private. Reflection shouldn’t be showing it, unless a part of Hibernate’s work on the POJO at load time reveals it…?
Thanks for your comments, guys!
May 20th, 2008 at 6:55 am
@James: Okay, tried it and it worked. Still need to figure out how.. Does it add accessors at runtime using bytecode enhancement? Hmm…
In any case, doesn’t this break encapsulation completely? For example, in one of my classes the actual collection handling is done by a support class. So, the setCollection of my real, hibernated class is delegating the call to the setCollection of the support class. In this example, access=field won’t work at all. Do you have an idea? As what I described above is not hypothetical at all..
May 20th, 2008 at 4:51 pm
http://www.databasesandlife.com/encapsulation-or-public-attributes-but-nothing-inbetween/
(Oh if only things like pingback and trackback would just work!)
May 20th, 2008 at 6:05 pm
It probably uses the setAccessible() method on java.lang.reflect.Field. If you make a field accessible, you can set it using reflection even if it’s private. Breaks encapsulation for sure, but a useful trick for injection frameworks.
May 20th, 2008 at 7:27 pm
@Brian: You’re right – but as I remember, and correct me if I’m wrong, it also requires special permissions (a specific flag in ReflectPermission?)
May 20th, 2008 at 8:31 pm
First comment gave me a connection error so sorry if this is a duplicate. The reflection api gives access to a classes private members via the getDeclaredFields method:
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html#getDeclaredFields()
… “This includes public, protected, default (package) access, and private fields,…”
May 20th, 2008 at 9:05 pm
@All: I’m sorry about the connection problems… I should really switch the current hosting plan to a better one.
@Paul: You’re right, but that still doesn’t give you the access required to write to the fields…
May 21st, 2008 at 6:05 pm
@James – Nice one, I hadn’t thought of doing that.
What I always do, to implement the paranoia approach with Hibernate, is to create “private” getters and setters for the actual List or whatever. That way noone can call them, but it works just fine with Hibernate. Then create other methods addItem or whatever, that I want other people to call.
May 22nd, 2008 at 5:15 am
@Adrian: I actually like your approach better than James’, as it allows me to have the collection contained in some other support class (see my answer to James)
Still, I think it breaks encapsulation, and makes it harder to unit-test the code, as it would be impossible to test the “setCollection” method, it being private.
May 25th, 2008 at 2:36 pm
This is an area where closures shine, they preserve encapsulation and would save memory, of course they don’t solve the issue with frameworks that need to muck with your collection
May 26th, 2008 at 9:49 pm
@Khalil: while I agree that closures shine on many fields, I fail to see how they help with this specific case… Can you please elaborate?
May 27th, 2008 at 12:14 am
Rather than providing a List getElements() kind of method you would have BGGA style http://www.javac.info
withElements( {Element=>void throws X} closure){
for(Element elt:elements) closure.invoke(element);
}
in client code you would write control invocation syntax
for withElements(Element elt){
System.out.println(elt);
}