To start off, this is more of a best-practice question than anything.
Introduction to the Environment
I have myself an abstract class. Let's call this class Item
. I also have another abstract class, we'll call this one Container
. The Container
class inherits the Item
class.
Here's my dilemma: I'm trying to right-properly design the OOP portions of it, and I'm having a bit of an issue due to my desire to abstract away as much as possible, while still creating maintainable and understandable code.
Let's start with some code:
public abstract class Item
{
private Guid _Guid;
private float _Weight;
public Guid Guid { get { return _Guid; } set { _Guid = value; } }
public float Weight { get { return _Weight; } set { _Weight = value; } }
public virtual ItemType ItemType { get { return ItemType.None; } }
}
public abstract class Container : Item
{
private float _MaxWeight;
private List<Item> _Items;
public List<Item> Items { get { return _Items; } }
public float MaxWeight { get { return _MaxWeight; } set { _MaxWeight = value; } }
public abstract ContainerType ContainerType { get; }
public sealed override ItemType ItemType { get { return ItemType.Container; } }
}
So, with this code the intention is for a Container
to be able to be treated as an Item
, with a few exceptions. (The container will have additional properties, etc.)
The issue, is that not all Item
objects can be added to a Container
. In fact, only Container
objects that have a smaller Container.MaxWeight
or non-Container
Item
objects can be added.
As of now, this restriction cannot be placed. My initial thought was to create an ItemCollection
inside the Container
class, that would essentially hold all of the Item
objects within it. This would allow me to implement my own ItemCollection.Add
method which would perform this filter.
The issue comes into play when I introduce my third class. Let's call it a Backpack
. This inherits Container
and looks as follows:
public class Backpack : Container
{
public sealed override ContainerType ContainerType { get { return ContainerType.Backpack; } }
}
Mind you - the Backpack
(as we are calling it) will only be able to hold Item
objects, or Container
objects with a smaller Container.MaxWeight
in them.
This also means that I may yet have other classes (let's use, for example, Pouch
) that will also inherit Container
, but which cannot contain other Container
objects.
Lastly, there may also be additional classes that cannot hold Item
objects, but only hold Container
objects. (See, for example, Inventory
.)
/// <summary>
/// Represents a collection of <see cref="Container"/> objects owned by an <see cref="Actor"/>.
/// </summary>
public class Inventory : Container
{
public sealed override ContainerType ContainerType { get { return ContainerType.Inventory; } }
}
Question Statements
Is this the best way to do it? Should I implement the ItemCollection
as I intended? What about Container
objects that can only hold other Container
objects?
Should I use the ContainerType.Inventory
to indicate that this object can only hold other Inventory
objects? Should I use the ContainerType.MaxWeight
value on it instead? (Any objects that can only hold Container
objects won't really have a MaxWeight
. The MaxWeight
an Actor
can hold is determined by their fitness.)
Should I make an additional class that inherit Container
that can only hold other Container
objects, then inherit Inventory
from this? Likewise, should I create another class inheriting from Container
that allows or disallows the addition of Item
objects only?
Additional Notes/Jargon
The idea is to force the programmers (mostly me) to properly handle each Container
and Item
as they should be. The issue lies in the fact that there are different types of Container
objects. Also, do note that this is not all of the code involved, but the additional code I excluded is irrelevant to this issue. Also note, the two additional classes I mentioned (Inventory
and Backpack
) are completely concrete. I will be working directly with those. I also want to have common infrastructure between them, so that any Item
that is a Container
can be worked in a general manner. (Rather than casting to the appropriate concrete class.)