views:

209

answers:

3

Hi

In a new Java project I try to apply as much best practices as possible. The one I'm having problems with is immutability. Although I understood the concept and already built some immutable classes I now came to a class where I think it's more appropriate to do it as a mutable class.

The main problem is that I want to hide the mutable parts of the class in some cases so that in my case the view layer of MVC can't directly modify objects but has to go through its controller.

I thought of 2 ways to do it:

  1. Create an interface "Thing" which has all immutable methods in it (read-only) and create an interface "MutableThing" which has the setters.

  2. Put all methods in one interface and restrict access to mutating methods by wrapping the object like the Collections.unmodifiableList(obj) method so that exceptions are thrown when accessing mutating methods.

I would prefer the first one because I think it's cleaner and more well designed but I have one problem with that. I have addListener(l) and removeListener(l) in the "Thing" interface so the view layer can register itself as a listener to some model objects. But with these two methods in the "Thing" interface it just doesn't make sense on its own anymore. Why would an interface have the ability to register listeners which signal data changes if there is no method to actually change data? I could put these methods in the "MutableThing" interface but the view layer only has access to the "Thing" interface and couldn't register itself as listener.

The point why this isn't working is just because of the listeners, is the view layer actually responsible for registering itself as listener on the model? If the controller could do it somehow (which has access to the "MutableThing") then there wouldn't be a problem but I wouldn't know how to realize it.

What would you suggest?

Thanks!

A: 

In my opinion, both of the two possibilities that you mentioned are valid ways to solve the problem. With regard to the first method: I assume you mean that interface MutableThing extends interface Thing to add the mutator methods.

With regard to the listener stuff: There are ofcourse several ways to do this. You could separate this from the Thing and MutableThing interface, for example by using Java's java.util.Observable class and java.util.Observer interface (or something similar you write yourself - those classes are not type-safe, because they're from before generics were added to Java).

Jesper
What do you mean by separating the listeners from the interfaces? How exactly would this look like?
neo
I mean that you do not put the `addListener` and `removeListener` methods in the `Thing` or `MutableThing` interfaces, but that you use a separate interface (for example `Observer` / `Observable`) for the listener functionality.
Jesper
+2  A: 

Why would an interface have the ability to register listeners which signal data changes if there is no method to actually change data?

All the interface states is that you can retrieve the following values. It does not imply that the object is immutable. When the object is mutable it is IMO a good idea to make sure everyone knows it as immutable objects have some properties that your object might not have (thread safe, safe to cache results etc). Your object is mutable. You should not pretend it is immutable. As such go with 1 and have add/removes on the Thing interface.

mlk
I was going to mention this, but you've already done so. Not an answer to the question, per-se, otherwise +1
ptomli
+1 - clearly the OP is confusing "immutable" with "mutable but not using this API". Understanding this confusion will help the OP figure out a coherent API design.
Stephen C
Thanks for your answers and comments! Ok so I understand what you mean by confusing both terms, is it then generally a bad idea to name interfaces as mutable* or immutable* because it should belong to its implementation? I think so. Nevertheless, what to do with the setters which shouldn't be exposed in the view layer? Leave in MutableThing?
neo
Personally I'm not a fan of putting such meta data (mutability, if it is an interface etc) in the class name. More so if that could lie. Depending on how heavy this class is I would just have the interface Thing and the implementation (with the sets).
mlk
+1  A: 

I'd go for a modified first attempt.

You may define your immutables by an interface, which contains all the getters. A mutable class implements the Immutable interface and contains all the required set methods without having these specified by an interface. Within your code you only access instances via the Immutable interface. The places in your code, where you actually need to create or modify the classes, you (exclusively) refer to those mutable classes by simply casting or by making them visible by putting those modifying implementations in the same package as a the mutable classes. A bundle concept like in osgi/eclipse could be helpful here, because you find support in hiding implementations etc.

Additionally you may use a seal() method to seal instances once you have created (and modified them). Whenever a set-method is called, you check, whether the instance isn't sealed. This might help to prevent modifications (or when working in a big team).

pimpf0r
Actually I thought of this too but wasn't quite sure if this is great OO design? Ok, if all of this happens within one bundle that's ok, but what if someone else (another bundle or whatever) wants to set some value. It cannot cast because it doesn't know the implementation. What I'm getting at: Shouldn't setters also be in interfaces? If not, in which cases not?
neo
Hey neo, I thought you said something about Immutable? Serious: If you plan to make those classes mutable, then a *set*-oriented interface makes sense here. But then again: you may drop the whole "get"-/"set"-Interface-parade and go for a sealing pattern to avoid later changes.
pimpf0r
I think in my case the setter I have belongs strongly to business logic and has to be accessable by others through an interface. I can't use seal because it should actually be always modifiable. I still would make it after my original first attempt, it seems to work quite good. I will let you know if I get into serious trouble with it :P
neo
hehe. I don't see serious trouble here at all. ;)
pimpf0r