There is a powerful and simple concept in programming that is widely 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 important argument in favor of 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 access to resources (objects or others OS resources). Why is it 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 accessed by threads, doesn’t change? There is no more need for synchronization!
The Class of the object referenced is immutable if it has no public fields, no methods that can change its internal data and no way for methods of the derived class to change its internal data. And because the value cannot change, it’s possible to reference the same object in all cases. There’s no need to have a copy constructor or assignment operator. For this reason, it’s recommended to make the copy constructor and assignment operator private, inherit from boost::noncopyable, or use the new C++11 feature “Explicitly defaulted and deleted special member functions“.
Even if a C++ class is not designed to be immutable, adding the const keyword to variables of his type will work. However, this solution has some drawbacks:
- You have to add const for each variable declaration we want to be immutable; sometimes forgetting it will generate unsuspected bugs in multithreading context.
- Even if a variable is declared as const, it might still be possible to mutate the object within a const method, in case of some fields are declared mutable. In such a case the object state can be modified and we are then talking of mutable object.
For example, the String class is well-known as an immutable in several other languages like C#, Java, and D. This class is by nature very popular and making it immutable is suitable for multithreading context. std::string is not immutable in C++ and the alternative is the use of the const keyword.
Domain Driven Design and Immutability
The Domain Driven Design is a software design approach, 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. The model should represent knowledge of the business.
In general, the shared data between threads 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.
Another benefit of immutable classes is that they can never violate LSP (Liskov Substitution Principle), here’s a definition of LSP quoted from its wiki page:
Liskov’s notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
For immutable objects, the state can’t change so the properties can’t be altered.
Detect Immutable classes in C++ source code
Immutability is a feature that can be enforced at compile-time. In other words, it can be enforced by static analysis tools. CQLinq that comes with the static analysis tool CppDepend proposes an IsImmutable condition that applies to types. To know which types of your code base are immutable :
from t in Types where t.IsImmutable select t
Beside the IsImmutable condition on types, we can also use two interesting conditions:
ChangesObjectState and ChangesTypeState. As the name suggests, 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 methods that assign an instance field of its parent type.
The condition ChangesTypeState matches methods that assign a static field of its parent type.
These 2 conditions can be used to filter at a glance which methods can change the state of the program, in other words, which methods provoke 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 select m
Conclusion
Eliminate the side-effects makes composing and integrating units together much easier and more robust, and simplify multithreaded programming. Immutable classes, and pure methods are the key to eliminate side effects.