You may be confusing classes, which implement behaviour, and class instances, which can hold data which distinguishes one instance of a class from another.
For the simple situation you describe, you can create one basic category class, which defines the behaviours of
- Having a name.
- Having an id.
- Containing a (possibly empty) list of items.
- Having a (possibly empty) list of subcategories.
*Having a name" means allowing the name to be read and (optionally) updated. Same for the lists.
You could also give it a
parent id but this is not necessary if you always start from your top level category and work down - it depends on how you traverse the structure. For the moment, let's assume you don't need it...
Anyway, having created this class, you then create successive instances of the class, giving each a name, adding items and subcategories etc.
Can you see that the behaviour for each instance is consistent? Each instance may have a unique
id, but the act of retrieving or updating that property is the same for all instances.
Now, if you do need your subcategories to know what their parent is, at this point I would consider having two different classes - one for top level categories and another for subcategories. While having one class and leaving the "parent" property empty in top level categories sounds simple, it opens the potential for error, while also burdening top-level categories with surplus fields and methods that they will (or should) never use. Consider these conflicting statements:
- A top-level category should never have a parent
- A sub-category always has a parent
- You make a category a sub-category by giving it a parent
- You can tell a top-level category because it has no parent
How do you avoid accidentally giving a top level category a parent? Or accidentally removing the parent of a subcategory? How do you detect these errors, after they have happened? The only way to tell a top-level from a sub category is the thing you just broke!
On the other hand, if you have separate classes for top categories and sub-categories, then you can have a top-level category class which simply doesn't have a
parent field or any parent-related methods, and a sub-category class which must have these. You never accidentally try to find out the parent of a top-level category because that is something only sub-categories can have.
Of course, now you have the problem of the shared behaviour of top-level and sub-categories. Once answer is to have a simpler
category class from which they both inherit. Another is to define a
category interface and have both the top and sub category classes implement it. Either way, code which only cares about general category behaviour can treat them both the same.
As for making
category management the responsibility of
items, I think you have that upside down. Categories need to know something about
items, because they contain them. In most situations,
items do not need to know anything about
categories. Categories are rarely essential properties of items; making your item implementation aware of categories means that if you change your category design, you have to also change your item design. This is called tight coupling, which is generally seen as bad practice. It may seem counter-intuitive at first, but it is generally better to design the different components of a system to know the least amount possible about other components, while exposing as little as possible about their own internal details. This is known as separation of concerns (or "loose coupling").
As a final note, do consider if the
id fields are necessary. If these categories are stored in a database, it may be necessary if this part of your code might also change the category names. But do remember that the "Bars" subcategory of "Bike Parts" can be uniquely identified as "/Bike Parts/Bars" and thus completely distinct from "/Venues/Bars". Even if the numerical id is there in the DB, you might not want it in your web code - or not everywhere, anyway. Consider whether you would want external sites linking to
If you haven't thought as far as persistence yet - or if the category structure would not be stored in a database - I would try implementing this without the ids, first.