Over the course of several projects, I developed a template for creating readonly objects and immutable object graphs. Immutable objects carry a 100 percent thread and, therefore, can be reused by thread. In my work, I very often use this template in web applications for configuration settings and other objects that I load and cache in memory. Cached objects should always be immutable, since you want to ensure that they are not unexpectedly changed.
Now you can easily create immutable objects, as in the following example:
public class SampleElement { private Guid id; private string name; public SampleElement(Guid id, string name) { this.id = id; this.name = name; } public Guid Id { get { return id; } } public string Name { get { return name; } } }
This is good for simple classes, but for more complex classes, I cannot imagine the concept of passing all values ​​through a constructor. It is desirable to have property setters, and your code creating a new object becomes easier to read.
So how do you create immutable objects with setters?
Well, in my template objects begin to change completely until you freeze them with one method call. Once an object is frozen, it will remain unchanged forever - it cannot be turned back into a mutable object. If you need a modified version of an object, you simply clone it.
Ok, now for some kind of code. In the following code snippets, I tried to weld a template to its simplest form. IElement is the basic interface that should ultimately implement all immutable objects.
public interface IElement : ICloneable { bool IsReadOnly { get; } void MakeReadOnly(); }
The Element class is the default implementation of the IElement interface:
public abstract class Element : IElement { private bool immutable; public bool IsReadOnly { get { return immutable; } } public virtual void MakeReadOnly() { immutable = true; } protected virtual void FailIfImmutable() { if (immutable) throw new ImmutableElementException(this); } ... }
Let us reorganize the SampleElement class above to implement an immutable object template:
public class SampleElement : Element { private Guid id; private string name; public SampleElement() {} public Guid Id { get { return id; } set { FailIfImmutable(); id = value; } } public string Name { get { return name; } set { FailIfImmutable(); name = value; } } }
Now you can change the Id property and the Name property until the object has been marked as immutable by calling the MakeReadOnly () method. Once it becomes immutable, calling setter will throw an ImmutableElementException.
Final note: The complete template is more complex than the code snippets given here. It also contains support for sets of immutable objects and complete object graphs of immutable object graphs. The full template allows you to turn the entire object graph immutable by calling the MakeReadOnly () method on the most distant object. When you start creating larger object models using this template, the risk of leaking objects increases. An object leak is an object that cannot call the FailIfImmutable () method before making changes to the object. To check for leaks, I also developed a common class of leak detectors for use in unit tests. It uses reflection to check if all properties and methods throw an ImmutableElementException in an immutable state. In other words, TDD is used here.
I loved this template very much and found great advantages in it. So what would I like to know if any of you use similar patterns? If so, do you know of any good resources that document this? I am essentially looking for potential improvements and any standards that may already exist in this thread.