May 19

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?

Related Posts with Thumbnails
Share

16 Responses to “Exposing collections: paranoia vs trust approaches”

  1. Paul Says:

    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.

  2. Aviad Says:

    @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?

  3. Daniel Pitts Says:

    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.

  4. James Says:

    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 :-)

  5. Aviad Says:

    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!

  6. Aviad Says:

    @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.. :)

  7. Adrian Smith Says:

    http://www.databasesandlife.com/encapsulation-or-public-attributes-but-nothing-inbetween/

    (Oh if only things like pingback and trackback would just work!)

  8. Brian Duff Says:

    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.

  9. Aviad Says:

    @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?)

  10. Paul Says:

    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,…”

  11. Aviad Says:

    @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…

  12. Adrian Smith Says:

    @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.

  13. Aviad Says:

    @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.

  14. Khalil Says:

    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

  15. Aviad Says:

    @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?

  16. Khalil Says:

    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);
    }