There is a powerful and simple concept in programming that is really underused: Immutability.
Basically, an object is immutable if its state doesn’t change once the object has been created. Consequently, a class is immutable if its instances are immutable.
There is one killer argument for using immutable objects: It dramatically simplifies concurrent programming. Think about it, why does writing proper multithreaded programming is a hard task? Because it is hard to synchronize threads accesses to resources (objects or others OS things). Why it is hard to synchronize these accesses? Because it is hard to guarantee that there won’t be race conditions between the multiple write accesses and read accesses done by multiple threads on multiple objects. What if there are no more write accesses? In other words, what if the state of the objects threads are accessing, doesn’t change? There is no more need for synchronization!
Immutable classes are also well adapted to be key in hashtables,The objects on which the hash values are computed must be immutable to make sure that the hash values will be constant in time. Indeed, hash value is computed from the state of the object.
A famous immutable class
There is one famous immutable class: System.String. When you think that you are modifying a string, you actually create a new string object. Often, we forget about it and we would like to write …
String str = ”foofoo”;
…where we need to write instead:
str = str.replace(“foo”, ”FOO”);
Of course, doing so comes at the cost of creating multiple string objects in memory when doing some intensive string computation. In this case you need to use the StringBuilder class.
Domain Driven Design and Immutability
The Domain Driven Design is an approach to the design of software, based on the two premises:
- That complex domain designs should be based on a model,
- And that for most software projects, the primary focus should be on the domain and domain logic (as opposed to the particular technology used to implement the system).
In other words the heart of the DDD is Model and the first thing to do when starting development is drawing the model. Model and design you create should shape each other. Model should represent knowledge of the business.
In general the shared data between thread concern the model entities. And making them immutable will eliminate side-effects. I couldn’t say it better than Wes Dyer so I quote him:
“We all know that generally it is not a good idea to use global variables. This is basically the extreme of exposing side-effects (the global scope). Many of the programmers who don’t use global variables don’t realize that the same principles apply to fields, properties, parameters, and variables on a more limited scale: don’t mutate them unless you have a good reason.(…)”
One way to increase the reliability of a unit is to eliminate the side-effects. This makes composing and integrating units together much easier and more robust. Since they are side-effect free, they always work the same no matter the environment. This is called referential transparency.
Immutability support in JArchitect and CQLinq
As we saw, immutability is a feature that can be enforced at compile-time. In other words it can be enforced by static analysis tools. Thus, the Code Query on LINQ feature (CQLinq) that comes with the static analysis tool JArchitect has an IsImmutable condition that applies on types.
To know which types of your code base are immutable it is as easy as writing this CQLinq query:
from t in Types where t.IsImmutable select t
To constraint a particular type MyNamespace. Foo to be immutable you can write this CQLinq constraint:
warnif count != 1
from t in Types where t.IsImmutable && t.FullName==”MyNamespace.Foo”
To constraint a range of types used by the class MyNamespace. Foo to be immutable:
warnif count != 1
from t in Types where t.IsImmutable && t.IsUsedBy(”MyNamespace.Foo”)
We can search for mutable classes derived from a class.
from t in Types where t.DepthOfDeriveFrom(“Foo.ImmutableClass”)>=0 && ! t.IsImmutable
And also all mutables classes inside a namespace hierarchy.
from t in Application.Namespaces.WithNameWildcardMatchIn( “Foo.Model”).ChildTypes() where t.DepthOfDeriveFrom(“Foo.ImmutableClass”)>=0 && ! t.IsImmutable
So, what’s behind the IsImmutable CQLinq condition? Here are the rules that JArchitect uses to decide if a type is immutable or not:
- A type with at least one non-private instance field is considered as mutable (because such a field can be eventually modified outside of the type).
- A type that has a method that is not a constructor and that assign an instance field is considered as mutable.
- A class that derives directly or indirectly from a mutable type is considered as mutable.
- Enumeration, static type, type defined in third projects are never considered as immutable. Although these types might match the definition of immutability, considering them as immutable would disturb developers while they care for their own immutable types.
- Particularly, classes that derive directly or indirectly from a class defined in a third project are never considered as immutable.
Beside the IsImmutable condition on types, we also added 2 conditions on methods: ChangesObjectState and ChangesTypeState.
As the name suggest, ChangesObjectState match methods that are assigning an instance field of its class and the ChangesTypeState match methods that are assigning a static field of its class.
The condition ChangesObjectState matches constructors and static method. The condition ChangesTypeState matches constructors, instance methods and static methods.
These 2 conditions can be used to see at a glance which methods can change the state of your program, in other words, which methods are mutable or non-const, or better said, which methods provoke side-effects.
And as Wes Dier wrote: One way to increase the reliability of a unit is to eliminate the side-effects.
Typically a method that doesn’t provoke side-effects is said to be a pure method. To match pure methods with CQLinq one can write:
from m in Methods
where !m. ChangesObjectState && !m. ChangesTypeState && !m.IsConstructor
Eliminate the side-effects makes composing and integrating units together much easier and more robust, and simplify multithreaded programming.