I want to make my class immutable. Obvious way would be to declare all fields as get; private set;
and to initialize all fields in constructor. So clients must provide everything in constructor. The problem is that when there are ~10 or more fields passing them in constructor become very unreadable, because there are no labels for each field.
For example this is pretty readable:
info = new StockInfo
{
Name = data[0] as string,
Status = s,
LotSize = (int)data[1],
ISIN = data[2] as string,
MinStep = (decimal)data[3]
};
compare to this:
new StockInfo(data[0] as string, s, (int) data[1], data[2] as string, (decimal) data[3])
And now imaging that I have 10 or more parameters.
So how can I make class immutable saving readability?
I can suggest only use the same formatting when using constructor:
info = new StockInfo(
data[0] as string, // Name
s, // Status
(int)data[1], // LotSize
data[2] as string, // ISIN
(decimal)data[3] // MinStep
);
Can you suggest something better?
To create an immutable class in Java, you have to do the following steps. Declare the class as final so it can't be extended. Make all fields private so that direct access is not allowed. Don't provide setter methods for variables.
If you want to encapsulate a mutable object into an immutable one, then you need to: Create a copy of the mutable object (i.e. via copy constructor, cloning, serialization/deserialization, etc.); never store the reference to the original mutable object. Never return the mutable object.
No, it is not mandatory to have all properties final to create an immutable object. In immutable objects you should not allow users to modify the variables of the class. You can do this just by making variables private and not providing setter methods to modify them.
Here is how you could do it, using C#'s named parameters:
var info = new StockInfo
(
Name: data[0] as string,
Status: s,
LotSize: (int)data[1],
ISIN: data[2] as string,
MinStep: (decimal)data[3]
);
Here are some options. You will have to decide what's best for you:
Use a classic immutable object (with a massive constructor) with named arguments for readability. (Drawbacks: Some frown on having many constructor arguments. May be inconvenient to use from other .NET languages without support for named arguments.)
info = new StockInfo
(
name: data[0] as string,
status: s,
...
)
Expose the mutable object through an immutable interface. (Drawbacks: The object could still be mutated with casting. Extra type to write.)
public interface IStockInfo
{
string Name { get; }
string Status { get; }
}
IStockInfo info = new StockInfo
{
Name = data[0] as string,
Status = s,
...
}
Expose a read-only view of the mutable object - see ReadOnlyCollection<T>
for example. (Drawbacks: Extra type to implement. Extra object created. Extra indirections.)
var readOnlyInfo = new ReadOnlyStockInfoDecorator(info);
Expose an immutable clone of the mutable object. (Drawbacks: Extra type to implement. Extra object created. Copying required.)
var immutableInfo = new ImmutableStockInfo(info);
Use freezable objects. (Drawback: Post-freeze mutation-attempts won't be caught until execution-time.)
info.Freeze();
info.Name = "Test"; // Make this throw an exception.
Use fluent-style builders or similar (Drawbacks: Some may be unfamiliar with the pattern. Lots of extra code to write. Lots of copies created. Intermediate states may possibly be illegal)
info = StockInfo.FromName(data[0] as string)
.WithStatus(s) // Make this create a modified copy
.WithXXX() ;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With