Reputation: 43
So let's say I have two classes, Food
and Barrel
for example. Barrel
contains an array of food private Food[] stores
to track what's stored in it. Barrel
also contains methods to access and modify stores
, such as public void Insert(Food)
and public void Remove(Food)
.
So Barrel
can store Food
. But I also want Food
to know where it's stored in, if anywhere. For example, I want to have a method Food.Rot()
, which only rots food if it's not stored.
My current solution is to have a property public Barrel Container { get; set; }
in Food
that tracks the instance of Barrel
it's stored in, and have Barrel.Insert(Food toAdd)
set toAdd.Container
to itself and Barrel.Remove(Food toRemove)
set toRemove.Container
to null
(under which condition Food.Rot()
would actually rot the food).
However, any other class can also access Food.Container
, and if they write to it, the original Barrel
will still have this Food
instance in stores
, but Food.Container
will no longer reflect that.
I can make the set method ensure that when Food.Container
changes, it first calls Container.Remove(this)
, but I really don't want anything besides Barrel
to be able to set Food.Container
in the first place.
I could also make it so Food
doesn't store its container at all and simply have Rot()
call a method bool IsStored()
to check every single Barrel.stores
to see if the instance of the Food
class that called it was stored inside, that way there's no two-way malarkey, but that seems just terrible for optimization.
It feels like somewhere along this design I've violated OOP and I expect that ideally this whole situation needs to be redesigned. I'm more than open to such feedback as well.
Upvotes: 0
Views: 1733
Reputation: 1197
When Food added into a Barrel you must modify the Barrel attribute of the Food. Also when the Barrel attribute is set you must add the food into the Barrel if not exists. Checking exists is important because otherwise, these two will call each other to cause a StackOverflow Exception.
You must also repeat the same approach when removing. Here is the sample you need:
public class Food
{
public string Name { get; set; }
private Barrel _barrel;
public Barrel Barrel
{
get
{
return _barrel;
}
set
{
if (_barrel != null && _barrel != value)
{
_barrel.Remove(this);
}
if (value != null)
{
value.Insert(this);
}
_barrel = value;
}
}
public override string ToString()
{
return Name+"@"+ Barrel??"";
}
}
public class Barrel
{
public string Name { get; set; }
public List<Food> Stores { get; set; } = new List<Food>();
public void Insert(Food food)
{
if (!Stores.Contains(food))
{
Stores.Add(food);
food.Barrel = this;
}
}
public void Remove(Food food)
{
if (Stores.Contains(food))
{
Stores.Remove(food);
food.Barrel = null;
}
}
public override string ToString()
{
return Name;
}
}
Test code:
Barrel b0 = new Barrel() { Name = "Barrel 0" };
Barrel b1 = new Barrel() { Name = "Barrel 1" };
//Use insert method.
Food fish = new Food() { Name = "Fish" };
//I will insert fish to b0 first.
b0.Insert(fish);
b1.Insert(fish);
//Assign barrel directly
Food chicken = new Food() { Barrel = b0, Name = "Chicken" };
//then change barrel
chicken.Barrel = b1;
Debug.WriteLine("Barrel 1:"+ string.Join(",",b1.Stores));
//Modify Barrel attribute
chicken.Barrel = null;
Debug.WriteLine("Barrel 1:" + string.Join(",", b1.Stores));
//Remove from stores
b1.Remove(fish);
Debug.WriteLine("Barrel 1:" + string.Join(",", b1.Stores));
Output:
Barrel 1:Fish@Barrel 1,Chicken@Barrel 1
Barrel 1:Fish@Barrel 1
Barrel 1:
Upvotes: 1