24

I love the immutable "pattern" because of its strengths, and in the past I have found it beneficial to design systems with immutable data types (some, most or even all). Often when I do so, I find myself writing fewer bugs and debugging is much easier.

However, my peers in general shy away from immutable. They are not inexperienced at all (far from it), yet they write data objects the classic way - private members with a getter and a setter for every member. Then usually their constructors take no arguments, or maybe just take some arguments for convenience. So often, creating an object looks like this:

Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");

Maybe they do that everywhere. Maybe they don't even define a constructor that takes those two strings, no matter how important they are. And maybe they don't change the value of those strings later and never need to. Clearly if those things are true, the object would be better designed as immutable, right? (constructor takes the two properties, no setters at all).

How do you decide if an object type should be designed as immutable? Is there a good set of criteria to judge it by?

I currently am debating whether to switch a few data types in my own project to immutable, but I would have to justify it to my peers, and the data in the types might (VERY rarely) change - at which time you can of course change it the immutable way (create a new one, copying the properties from the old object except for the ones that you want to change). But I'm not sure if this is just my love for immutables showing through, or if there is an actual need for/benefit from them.

2
  • 1
    Program in Erlang and the whole problem is solved, everything is immutable
    – Zachary K
    Feb 28, 2012 at 18:16
  • 1
    @ZacharyK I was actually thinking of mentioning something about functional programming, but refrained due to my limited experience with functional programming languages.
    – Ricket
    Feb 28, 2012 at 20:43

8 Answers 8

27

Seems like you're approaching it backwards. One should default to immutable. Only make an object mutable if you absolutely have to/just can't make it work as an immutable object.

11

The primary benefit of immutable objects is guaranteeing thread safety. In a world where multiple cores and threads is the norm, this benefit has become very important.

But using mutable objects is very convenient. They perform well, and as long as you're not modifying them from separate threads, and you have a good understanding of what you are doing, they are quite reliable.

7
  • 15
    The benefit of immutable objects is that they are easier to reason about. In multithreaded environment, this is awfully easier; in plain single-threaded algorithms, it is still easier. E.g. you never have to care whether the state is consistent, did the object pass all the mutations needed to be used in a certain context, etc. The best mutable objects allow you to care little about their exact state, too: e.g. caches.
    – 9000
    Feb 28, 2012 at 17:44
  • -1, I agree with @9000. Thread safety is secondary (consider objects that appear in immutable but have internal mutable state because of e.g. memoization). Also, bringing up mutable performance is premature optimization, and it's possible to defend anything if you require that the user "know what they're doing". If I knew what I was doing all the time, I'd never write a program with a bug.
    – Doval
    Mar 13, 2014 at 19:56
  • 4
    @Doval: There's nothing to disagree about. 9000 is absolutely correct; immutable objects are easier to reason about. That's partly what makes them so useful for concurrent programming. The other part is that you don't have to worry about the state changing. Premature optimization is irrelevant here; the use of immutable objects is about design, not performance, and poor choice of data structures up-front is premature pessimization. Mar 13, 2014 at 20:07
  • @RobertHarvey I'm not sure what you mean by "poor choice of data structure up front." There are immutable versions of most mutable data structures out there providing similar performance. If you need a list, and you have a choice of using an immutable list and a mutable list, you go with the immutable one until you know for sure it's a bottleneck in your application and the mutable version will perform better.
    – Doval
    Mar 13, 2014 at 20:12
  • 2
    @Doval: I've read Okasaki. Those data structures aren't commonly used unless you're using a language that fully supports the functional paradigm, like Haskell or Lisp. And I dispute the notion that immutable structures are the default choice; the vast majority of business computing systems are still designed around mutable structures (i.e. relational databases). Starting out with immutable data structures is a nice idea, but it's still very ivory tower. Mar 13, 2014 at 20:15
6

There are two major ways to decide if an object is immutable.

a) Based on the nature of the Object

It is easy to catch these situations because we know that these objects will not change after it is constructed. For example if you are have a RequestHistory entity and by nature history entities do not change once it is constructed. These objects can be straight forward designed as immutable classes. Keep in mind that Request Object is mutuable as it can change its state and whom it is assigned to etc over the time but request history do not change. For example, there was a history element created last week when it moved from submitted to assigned state AND this history entitiy can never change. So this is a classic immutable case.

b) Based on the design choice, external factors

This is similar to java.lang.String example. Strings can actually change over the time but by design, they have made it as immutable due to caching/string pool/concurrency factors. Similary the caching/concurrency etc can play a good role in making an object immuatble if caching/concurrency and related performance is vital in the application. But this decision should be taken very carefully after anlyzing all the impacts.

The main advantage of immutable objects is they are not subjected to tumble-weed pattern.i.e the object will not pick up any change over the life time and it makes the coding and maintenance very very easier.

4

I currently am debating whether to switch a few data types in my own project to immutable, but I would have to justify it to my peers, and the data in the types might (VERY rarely) change - at which time you can of course change it the immutable way (create a new one, copying the properties from the old object except for the ones that you want to change).

Minimizing the state of a program is highly beneficial.

Ask them if they want to use a mutable value type in one of your classes for temporary storage from a client class.

If they say yes, ask why? Mutable state doesn't belong in a scenario like this. Forcing them to create the state where it actually belongs and making the statefulness of your data types as explicit as possible are excellent reasons.

4

The answer is somewhat language-dependent. Your code looks like Java, where this problem is about as difficult as possible. In Java, objects can only be passed by reference, and clone is completely broken.

There is no simple answer, but for certain you want to make small-ish value objects immutable. Java correctly made Strings immutable, but incorrectly made Date and Calendar mutable.

So definitely make small value objects immutable, and implement a copy constructor. Forget all about Cloneable, it's so badly designed that it's useless.

For larger value objects, if it is inconvenient to make them immutable, then make them easy to copy.

2
  • Sounds a lot like how to choose between stack and heap when writing C or C++ :)
    – Ricket
    Feb 29, 2012 at 2:26
  • @Ricket: not so much IMO. Stack/heap depends on the object lifetime. It is very common in C++ to have mutable objects on the stack. Mar 14, 2014 at 18:45
1

And maybe they don't change the value of those strings later and never need to. Clearly if those things are true, the object would be better designed as immutable, right?

Somewhat counter-intuitively, never needing to change the strings later on is a pretty good argument that it doesn't matter if the objects are immutable or not. The programmer is already treating them as effectively immutable whether the compiler enforces it or not.

Immutability doesn't usually hurt, but it doesn't always help either. The easy way to tell if your object might benefit from immutability is if you ever need to make a copy of the object or acquire a mutex before changing it. If it never changes, then immutability doesn't really buy you anything, and sometimes makes things more complicated.

You do have a good point about the risk of constructing an object in an invalid state, but that's really a separate issue from immutability. An object can be both mutable and always in a valid state after construction.

The exception to that rule is that since Java supports neither named parameters nor default parameters, it can sometimes get unwieldy to design a class that guarantees a valid object just using overloaded constructors. Not so much with the two-property case, but then there's something to be said for consistency as well, if that pattern is frequent with similar but larger classes in other parts of your code.

2
  • I find it curious that discussions of mutability fail to recognize the relationship between immutability and the ways in which an object can be safely exposed or shared. A reference to a deeply-immutable class object can safely be shared with untrusted code. A reference to an instance of a mutable class may be shared if everyone who holds a reference can be trusted never to modify the object or expose it to code that might do so. A reference to a class instance that may be mutated should generally not be shared at all. If only one reference to an object exists anywhere in the universe...
    – supercat
    Nov 22, 2014 at 23:38
  • ...and nothing has queried its "identity hashcode", used it for locking, or otherwise accessed its "hidden Object features", then changing an object directly would be semantically no different from overwriting the reference with a reference to a new object which was identical but for the indicated change. One of the biggest semantic weaknesses in Java, IMHO, is that it has no means by which code can indicate that a variable should only the only non-ephemeral reference to something; references should only be passable to methods that can't possibly keep a copy after they return.
    – supercat
    Nov 22, 2014 at 23:43
0

I might have an overly low-level view of this and likely because I'm using C and C++ which don't exactly make it so straightforward to make everything immutable, but I see immutable data types as an optimization detail in order to write more efficient functions devoid of side effects and be able to very easily provide features like undo systems and non-destructive editing.

For example, this could be enormously expensive:

/// @return A new mesh whose vertices have been transformed
/// by the specified transformation matrix.
Mesh transform(Mesh mesh, Matrix4f matrix);

... if Mesh isn't designed to be a persistent data structure and was, instead, a data type which required copying it in full (which could span gigabytes in some scenario) even if all we're going to be doing is changing a part of it (like in the above scenario where we only modify vertex positions).

So that's when I reach for immutability and design the data structure to allow unmodified portions of it to be shallow copied and reference counted, to allow the above function to be reasonably efficient without having to deep copy entire meshes around while still being able to write the function to be free of side effects which dramatically simplifies thread-safety, exception-safety, the ability to undo the operation, apply it non-destructively, etc.

In my case it's too costly (at least from a productivity standpoint) to make everything immutable, so I save it for the classes which are too expensive to deep copy in full. Those classes are usually hefty data structures like meshes and images and I generally use a mutable interface to express changes to them through a "builder" object to get a new immutable copy. And I'm not doing that so much to try to achieve immutable guarantees at the central level of the class so much as helping me to use the class in functions that can be free of side effects. My desire to make Mesh immutable above is not directly to make meshes immutable, but to allow easily writing functions free of side effects that input a mesh and output a new one without paying a massive memory and computational cost in exchange.

As a result I only have 4 immutable data types in my entire codebase, and they're all hefty data structures, but I use them heavily to help me write functions which are free of side effects. This answer might be applicable if, like me, you're working in a language that doesn't make it so easy to make everything immutable. In that case, you can shift your focus towards making the bulk of your functions avoid side effects, at which point you might want to make select data structures immutable, PDS types, as an optimization detail to avoid the expensive full copies. Meanwhile when I have a function like this:

/// @return The v1 * v2.
Vector3f vec_mul(Vector3f v1, Vector3f v2);

... then I have no temptation to make vectors immutable since they're cheap enough to just copy in full. There's no performance advantage to be gained here by turning Vectors into immutable structures that can shallow copy unmodified parts. Such costs would outweigh the cost of just copying the entire vector.

-1
Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");

If Foo only has two properties, then it's easy to write a constructor that takes two arguments. But suppose you add another property. Then you have to add another constructor:

public Foo(String a, String b, SomethingElse c)

At this point, it's still manageable. But what if there are 10 properties? You don't want a constructor with 10 arguments. You can use the builder pattern to construct instances, but that adds complexity. By this time, you'll be thinking "why didn't I just add setters for all the properties like normal people do"?

5
  • 4
    Is a constructor with ten arguments worse than the NullPointerException that occurs when property7 was not set? Is the builder pattern more complex than the code to check for uninitialized properties in each method? Better to check once when the object is constructed. Feb 28, 2012 at 17:01
  • 2
    As it was said decades ago exactly for this case, "If you have a procedure with ten parameters, you probably missed some."
    – 9000
    Feb 28, 2012 at 17:47
  • 1
    Why wouldn't you want a constructor with 10 arguments? At what point do you draw the line? I think a constructor with 10 arguments is a small price to pay for the benefits of immutability. Plus, either you have one line with 10 things separated by commas (optionally spread into several lines or even 10 lines, if it looks better), or you have 10 lines anyway as you set up each property individually...
    – Ricket
    Feb 29, 2012 at 2:25
  • 1
    @Ricket: Because it increases the risk of putting the arguments in the wrong order. If you're using setters, or the builder pattern, that's pretty unlikely. Feb 29, 2012 at 4:49
  • 4
    If you have a constructor with 10 arguments, perhaps it's time to think about encapsulating those arguments into a class (or classes) of their own and/or taking a critical look at your class design.
    – Adam Lear
    Mar 14, 2012 at 1:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.