patterncsharpMinor
Table builder pattern
Viewed 0 times
patterntablebuilder
Problem
Trying to combine functional style (immutable objects) and flexibility of property setters. For the sake of example, let’s say we have a soil types table with two attributes: Color and Name. I am looking for a way to alternate Names, but not Color. Here is how I solved it:
Where this interface is immutable:
And this class is mutable:
And this non-generic class is immutable:
```
public class SoilTypes : SoilTypes
{
public static SoilTypes Default = new SoilTypes(
(White, "Undefined"),
(Red, "Sensitive Fines"),
(Green, "Organic Soil"),
(Blue, "Clay"),
(Orange, "Silty Clay"));
public SoilTypes(params SoilType[] types)
: base(types)
{
}
public SoilTypes With(Action> update)
{
var copy = this
.Select(t => new SoilType(t))
.ToArray();
update(new
// retrieving: all objects are immutable
SoilTypes types = SoilTypes.Default;
ISoilType clay1 = types.Clay;
ISoilType clay2 = types[3];
// derive an alternated immutable copy
SoilTypes altTypes = types
.With(tt =>
{
// tt.SensitiveFines.Color is still read only
tt.SensitiveFines.Name = "Very sensitive fines!";
tt[2].Name = "Purely Organic soil!";
});
// retrieving: everything is immutable
ISoilType sensitiveFines = altTypes.SensitiveFines;Where this interface is immutable:
public interface ISoilType
{
Color Color { get; }
string Name { get; }
}And this class is mutable:
public class SoilType : ISoilType
{
public static implicit operator SoilType((Color Color, string Name) tuple) =>
new SoilType(tuple.Color, tuple.Name);
internal SoilType(ISoilType source)
: this(source.Color, source.Name)
{
}
internal SoilType(Color color, string name)
{
Color = color;
Name = name;
}
public Color Color { get; }
public string Name { get; set; }
}And this non-generic class is immutable:
```
public class SoilTypes : SoilTypes
{
public static SoilTypes Default = new SoilTypes(
(White, "Undefined"),
(Red, "Sensitive Fines"),
(Green, "Organic Soil"),
(Blue, "Clay"),
(Orange, "Silty Clay"));
public SoilTypes(params SoilType[] types)
: base(types)
{
}
public SoilTypes With(Action> update)
{
var copy = this
.Select(t => new SoilType(t))
.ToArray();
update(new
Solution
I'm afraid this is not fully immutable becasue I am able to change the
The underlying data type is still
Consider a user that writes a function like this one because he doesn't like interfaces :-)
and calls it
Name with a simple cast:altTypes.Dump();
((SoilType)altTypes.SensitiveFines).Name = "foo";
altTypes.Dump();The underlying data type is still
SoilType so the interface does not protect the data from being overriden.Consider a user that writes a function like this one because he doesn't like interfaces :-)
public static void foo(SoilType bar)
{
bar.Name = "new name";
}and calls it
foo((SoilType)altTypes.SensitiveFines);
altTypes.Dump();Name changed. Unfortunatelly I have no idea how to prevent it yet.Code Snippets
altTypes.Dump();
((SoilType)altTypes.SensitiveFines).Name = "foo";
altTypes.Dump();public static void foo(SoilType bar)
{
bar.Name = "new name";
}foo((SoilType)altTypes.SensitiveFines);
altTypes.Dump();Context
StackExchange Code Review Q#161521, answer score: 4
Revisions (0)
No revisions yet.