While trying to model my domain, I came across the following problem. Let’s image we have a Thing:
class Thing
{
public int X { get; set; }
}
Things have a property X. Then, there are Packs, which aggregate Things. But the domain requires that there is some restriction on the Things the Packs can hold. Let it be for example that the cumulative value of Xes can’t be higher then some specific limit:
class Pack
{
private readonly List<Thing> myThings = new List<Thing>();
private const int MaxValue = 5;
public void Add(Thing thing)
{
if (myThings.Sum(t => t.X) + thing.X > MaxValue)
throw new Exception("this thing doesn't fit here");
myThings.Add(thing);
}
public int Count
{
get { return myThings.Count; }
}
public Thing this[int index]
{
get { return myThings[index]; }
}
}
So I check before adding a Thing to a Pack for the condition, but it’s still so easy to get into troubles:
var pack = new Pack();
pack.Add(new Thing { X = 2 });
pack.Add(new Thing { X = 1 });
var thingOne = new Thing { X = 1 };
var thingTwo = new Thing { X = 3 };
//pack.Add(thingTwo); // exception
pack.Add(thingOne); // OK
thingOne.X = 5; // trouble
pack[0].X = 10; // more trouble
In C++ the solution would be to make a copy upon insertion and return const reference in the indexer. How to design around this problem in C# (and probably Java)? I just can’t seem to think of a good solution:
- make Thing immutable – but what if it needs to be mutable?
- watch the Things in Pack with event/observer – but that means that Pack imposes the design of Thing; what if Things have more properties? Then I’ll end up with just one event due to the need for Pack to watch for changes – that seems awkward to me.
Any ideas or preferred solutions?
EDIT:
Coming back to this question… I’ve accepted the reply by Itay. He’s right.
The original issue was that from one context you would want a Thing object to be immutable, and from a different context, you would want it to be mutable. And that calls for a seperate interface… maybe. I said “maybe”, because most of the time, Pack would be an Aggregate of Things (in the DDD sense) and therefore be the owner of the objects – which means it should not give you the ability to change the owned object (either return a copy or return an immutable interface).
It’s nice that in C++ this particular thing can be handled so easily by the const modifier. Seems like a lot less coding if you want to keep things in a consistent state.
Make
Thingimmutable.In addition, instead of
if (myThings.Sum(t => t.X) + thing.X > MaxValue)I think it is better to hold a sum field in pack so you don’t have to recalculate the sum each time.EDIT
Sorry – I missed the fact that you stated that you need it mutable.
But… How would your c++ solution work? I don’t know much c++ but doesn’t c++ constant reference will prevent the change on instances that on the Pack?
EDIT2
Use an interface