µObjects: Immutability

How To Create Immutability for More Maintainability

Our goal as software developers is to produce code that can be easily maintained. This is frequently a challenge; as writing good code is hard.
µObjects is a way of Object Oriented Programming that I've found gives us this. There are a number of ways that µObjects improve the maintainability of code; but what are they and how do we create the objects to get these improvements? There's a number of guidelines in place for creating the µObjects that give you these. Today we'll dive into how to make our code Immutable.


This is a goal we have to simplify one of the most complicated things in computer science - Naming. For... No; not naming. Not this time. µObjects make some of it's own naming challenges; but easily refactored there.
What µObjects simplify is Threading. Thread blocking and ensuring you have the correct data is a giant pain to get right. Even harder to hunt down some random race condition through twenty classes all potentially mutating the data...

No More! We shouldn't do that. If the data can't change; then we can't hit those conditions. Make things unchanging; immutable.

Clearly this is a well known style to prevent the threading headache; but how do we handle it with µObjects?
A huge part of this is to use Extreme Encapsulation. We don't return our privates. We ask things to give us a result. This isn't the topic of this post; so we'll leave that with a reminder to Anthropomorphize.

When your class correctly encapsulates the data; it's never exposed. No one else actually touches the data. No one getting "your" data prevents them from ever needing to ensure they have "the correct information".
I want to repeat this point; when class data can't be seen, then no one has to worry about getting the "correct" data.
This is the whole world of semaphores and locks and the pain of threading - all because we allow others to look at our data.

When your data can't change, YOU never have to worry about the data being correct. This is huge. This simplifies so much in todays multithreaded applications.
If no one touches your unchanging data; they doubly never have to worry if it's safe to ask you a question - they can just ask!

Sure, sure, I've sold you on making your classes immutable... OK; fine - The luminaries have sold you on it; I'm just parroting. Polly want an immutable.
What I can tell you is ... what anyone familiar with your language of choice could... How to make your code immutable!

I've been applying µObjects in C#, as that's the primary workplace language, and in C# it's creating class variables as private readonly.

public class Simple{
  private readonly IFoo _foo;
  Simple(IFoo foo) => _foo = foo;
}

One small caveat to this is that an objects "state" should be immutable. I start off assuming all class variables are part of the state. I've encountered situations where I have to have a mutable variable; but it's never been part of what I'd consider the objects state. A frequent example of this is in the UI. The Control objects often need reference to their containing object or page. This isn't available at construction (C# readonly only allows setting from the constructor.) of the object, so I can't lock it down as tightly as I'd like. Even then; it's never changed.

public void Init(IPage page) => _page = page;

I could add exceptions of "OMG! Only set once!!!", but it's extra code we don't need yet. If it was a library, I totally would to have the object better protect itself. This isn't for this post; but protections should only be needed at the book ends. Once you control the code flow; you control how it can break; don't break.

I don't consider the parent of a UI control to be part of it's state. It is data the object needs to operate, and there's other ways it could be gotten; setting it is the least smelly.

The other exception I've encountered is caching. I have to set and unset the cache data. The Cache Data isn't the state though. The location is the state. If the location of the cache is immutable, then I still consider the cache immutable even though the cache value can change.

This is trick to immutability. Everything should be immutable. If it isn't - Is it part of the state?
The simplest way I've found to answer that question is, "Would I serialize it?" For a UI element, I won't serialize the parent control. That's not part of itself.
A cache object's state isn't the cache data; it's the location of the cache. Just serialize that and re-load the data.
In both of these cases; state is what you'll serialize. It's my litmus test, and I've yet to find exception or better.

This is Immutability for µObjects. The same way you'd do for any other class in the language.
One of the components of something being a µObject is that they are immutable. While I say, "Encourage Immutability", if it isn't really immutable; it's not really a µObject.
One of the things we should all be striving for in OOP is immutability. When we do procedural code in objects, mutability is almost a must for the class. This mutability forces more code into existence to accommodate that it's mutable. To make it thread safe around the changes... Mutable classes are code with hidden bugs.

The inability to re-assign; which is the extreme of the functional paradigm gives us classes which are immutable.

As the Sample class above; and C# helps enforce; immutability in µObjects is our application of Secondary and Primary Constructors. This is a concept I was working towards with my Double Constructor post. Reading Elegant Objects really solidified it with section 3.6. Which unfortunately doesn't have a link to a post by Yegor about primary and secondary constructors. Book's worth it; check it out some of the stuff he has posts about here

Short version better is that a µObject can only use interfaces passed into it's Primary Constructor.

public class MyThing
{
    public MyThing(IFoo foo, IBar bar){}
}

These parameters are then assigned to read only variables, or whatever language construct exists.

public class MyThing
{
    private readonly IFoo _foo;
    private readonly IBar _bar;
    public MyThing(IFoo foo, IBar bar){
        _foo = foo;
        _bar = bar;
    }
}

This sets us up MyThing to be immutable; but it's consumers must know about every that it needs. This creates very unwieldy objects that are instantiated far from where, and even IF they are used.
This is where the "secondary" constructors come into play. They don't set any variables. They only new up objects. Nested new's are HIGHLY frowned upon as mentioned above, forces knowledge of it's implementation and construction onto it's consumer.
What this would look like for our example

public class MyThing
{
    private readonly IFoo _foo;
    private readonly IBar _bar;

    //Secondary Constructor
    public MyThing(): this(new Foo(), new Bar()){}

    //Primary Constructor
    public MyThing(IFoo foo, IBar bar){
        _foo = foo;
        _bar = bar;
    }
}

Secondary constructor don't set values, they don't DO anything. If it's doing something, it's doing it wrong; but this is a discussion for another post.
Constructors, in proper µObjects, will never throw or fail to instantiate. This is a very positive thing for out code, as it allows us to avoid a huge problem...

Summary

We've covered some wins of Immutability and how we can implement it in our C# code.
This is a huge simplification in the code I've been applying µObjects to. Not having to worry about changing state simplifies the picture we have to keep in our head.
With a decreased developer cognitive load, we can think about how to implement the task and the bigger picture around the actual task. We don't need to also try to think about what and where the data can be changed.
No more race condition bugs biting us in the ass.

I'm a huge fan of Immutability and love how easy µObjects allow; and encourage it.

Show Comments