In my opinion, your rule is a good one (or at least it's not a bad one), but only because of the situation you are describing. I wouldn't say that I agree with it in all situations, so, from the standpoint of my inner pedant, I'd have to say your rule is technically too broad.
Typically you wouldn't define immutable objects unless they are essentially being used as data transfer objects (DTO), which means that they contain data properties but very little logic and no dependencies. If that is the case, as it seems it is here, I'd say you are safe to use the concrete types directly rather than interfaces.
I'm sure there will be some unit-testing purists who will disagree, but in my opinion, DTO classes can be safely excluded from unit-testing and dependency-injection requirements. There is no need to use a factory to create a DTO, since it has no dependencies. If everything creates the DTOs directly as needed, then there's really no way to inject a different type anyway, so there's no need for an interface. And since they contain no logic, there's nothing to unit-test. Even if they do contain some logic, as long as they have no dependencies, then it should be trivial to unit-test the logic, if necessary.
As such, I think that making a rule that all DTO classes shall not implement an interface, while potentially unnecessary, is not going to hurt your software design. Since you have this requirement that the data needs to be immutable, and you cannot enforce that via an interface, then I would say it's totally legitimate to establish that rule as a coding standard.
The larger issue, though, is the need to strictly enforce a clean DTO layer. As long as your interface-less immutable classes only exist in the DTO layer, and your DTO layer remains free of logic and dependencies, then you will be safe. If you start mixing your layers, though, and you have interface-less classes that double as business layer classes, then I think you will start to run into much trouble.
I program in C# and Objective-C. I really like immutable typing, but in real life I've been always forced to limit its usage, mainly for data types, for the following reasons:
- Implementation effort comparing to mutable types. With an immutable type, you would need to have a constructor requiring arguments for all properties. Your example is a good one. Try imagining that you have 10 classes, each having 5-10 properties. To make things easier, you might need to have a builder class to construct or create modified immutable instances in a manner similar to
StringBuilder
or UriBuilder
in C#, or WeatherBuilder
in your case. This is the main reason for me as many classes I design are not worth such an effort.
- Consumer usability. An immutable type is more difficult to use in comparison to mutable type. Instantiation requires initialising all values. Immutability also means that we cannot pass the instance to a method to modify its value without using a builder, and if we need to have a builder then the drawback is in my (1).
- Compatibility with the language's framework. Many of the data frameworks require mutable types and default constructors to operate. For example, you cannot do nested LINQ-to-SQL query with immutable types, and you cannot bind properties to be edited in editors such as Windows Forms' TextBox.
In short, immutability is good for objects that behave like values, or only have a few properties. Before making anything immutable, you must consider the effort needed and the usability of the class itself after making it immutable.
Best Answer
A
Class
isn't truly immutable if any of this children references aren't immutable as well. If your rootClass
hasfinal
references to all its instance variables and all those instance variables are immutable as well as all their children following this same restriction then you can say the rootClass
is immutable.If any of the children references are non-
final
or any of the children instances are mutable then the containingClass
is not immutable.It doesn't matter about the internal immutablity whatever that means is anyone's guess, all that matters is that the public contract to the class immutability is kept. You can't have partial immutability anymore than you can be partially dead. Your class is either immutable or it isn't. And it isn't if any of its referred to classes or their classes, etc. are mutable.
If you have a graph and any of the members of the graph are mutable you can't deterministically say that you have an immutable state. Concurrency guarantees go out the window,
.equals()
and.hashCode()
become non-deterministic and simple to test cloning and serializing go out the window as well.If anything breaks this immutability contract you lose all the benefits of trying to maintain this immutability contract.
Simpler concurrency issues is a main motivational force of immutability.
Having side effect free code helps with predictability and maintainability. Since you don't have to wonder where things are getting mutated in the call tree because you know they can't be mutated.
Performance is another less important factor, in Java the JVM can make some highly optimized decisions about caching and other factors if it knows data can't change state. It does provide important hints to the compiler at compile time and the JIT algorithms at runtime.