Restrict Mutability of State
What appears at first to be a trivial observation turns out to be a subtly important one: a large number software defects arise from the (incorrect) modification of state. It follows from this that if there is less opportunity for code to change state, there will be fewer defects that arise from state change.
Perhaps the most obvious example of restricting mutability is its most complete realization: immutability. A moratorium on state change is an idea carried to its logical conclusion in functional programming, but even its modest application in the context of other programming models has a simplifying effect. If an object is immutable it can be shared freely across different parts of a program without concern for aliasing or synchronization problems. An object that does not change state is inherently thread-safe -- there is no need to synchronize state change if there is no state change. An immutable object does not need locking or any other palliative workaround to achieve safety. Immutability is particularly suitable for value objects in languages that favor predominantly reference-based semantics. For example, Java's
String class is essentially immutable -- if you want another string value, you use another string object.
A related solution based on partial immutability is qualification, such as the
const feature found in C++ and D. Object usage can be constrained with respect to the mutability declared for accessing the object. The set of operations on a qualified object is restricted to those that are similarly qualified.
Perhaps a little counterintuitively, copying offers a complementary technique for restricting mutability. In languages offering a transparent syntax for passing by copy, such as C#'s
struct objects and C++, copying value objects can greatly improve encapsulation and reduce opportunities for unnecessary and unintended state change. Consider an object holding another object by value as a field. A query that leads to the return of that field does not have to take any special measures to protect the value if it is returned by copy: the caller will have its own copy and cannot modify the internal state of the originating object. Note that to be effective for values copying requires a transparent passing syntax: if the programmer has to make special efforts to remember to make the copy, such as explicitly call a clone method, they are also being the opportunity to forget to do it. It becomes a complication that is easy to overlook rather than a simplification.
Another way to restrict the mutability of state is to reduce the scope in which it is accessed. Rather than declare a local variable at the top of a function or method long before it is used or can be properly initialized, declare it as late as possible in as narrow a scope as possible. Global variables have the broadest scope possible and are a problem for just this reason. What qualifies as global is not just restricted to classic global variables in procedural languages, but applies also to class- or module-wide variables that offer some form of public access.
Of course, this is not some kind of silver bullet with which to shoot down all defects, but restricting the mutability of state offers a guideline that can help to reduce the incidence of defects through improved encapsulation and code simplification.
This work is licensed under a Creative Commons Attribution 3
Back to 97 Things Every Programmer Should Know home page