0

So recently I've been learning more about C# OOP - specifically how to work with inheritance, composition and aggregation.

I'm doing this in the context of a practice task which asks to build a ordering API for a restaurant type service. Specifically, the task asks for a Menu. A Menu can be of multiple types, such as a Lunch Menu, Dinner Menu etc. Each Menu then has multiple Categories, such as Starters, Mains, Desserts etc. A category can then belong to multiple menus. Each category then also has subcategories, so for example a Starter can have hot and cold options. Inside a subcategory then has multiple food items inside it, so for hot items(subcategory) in the Starters(category) on the Lunch menu(menu) there can be soup, pizza etc.

From what I understand of the task and OOP, this will require composition rather than inheritance. This is as each Class HAS classes of the other type inside - a Menu HAS categories, category HAS subcategories etc.

So I have built the classes as follows:

The Menu class:

public class Menu { public Menu() {} public Menu(long Id, string Name) { this.id = Id; this.name = Name; } private long id; private string name; } 

The Category class:

public class Category { public Category() {} public Category(Menu menu, long categoryID, string Name) { this._menu = menu; this.CategoryID = categoryID; this.name = Name; } private Menu _menu; public long CategoryID { get; set; } private string name { get; set; } } 

The Subcategory class:

public class Subcategory { public Subcategory() {} public Subcategory(Category category, int subcategoryID, string Name) { this._category = category; this.SubcategoryID = subcategoryID; this.name = Name; } private Category _category; private long SubcategoryID { get; set; } private string name { get; set; } } 

And lastly the FoodItem class:

public class FoodItem { public FoodItem() {} public FoodItem(Subcategory subcategory, long Id, string Name, string Size) { this._subCategory = subcategory; this.Id = Id; this.name = Name; this.size = Size; } private Subcategory _subCategory; public long Id { get; set; } public string name { get; set; } public string size { get; set; } } 

So what I have done is following examples online - a instance of the superclass inside each subclass is created and passed into the constructor. This means a subclass cannot exist unless the superclass has also been created - a Category requires a Menu, a subcategory requires a category, and a fooditem requires a subcategory.

The questions I have are:

  • The issue I have now is with instantiating a FoodItem. To create a FoodItem, do I need to create a Menu object, then create a Category object and place the Menu object in, create a subcategory and place the Category in and then a FoodItem and place the subcategory in? Would that be correct? Or do I just create a FoodItem and the rest are automatically created? I think the first option is correct but would like to clarify.
  • Is my assumption that Composition is the right way to go here correct? Or is inheritance the better way to go?
  • For completing the task, I need to be able to create FoodItems and post to the API. Assuming my way of doing question 1 is correct, how would I go about creating the superclasses a FoodItem would need?

Any direction is much appreciated.

2 Answers 2

2

You've identified that your menu HAS categories, but have implemented it the other way around (in your code, your category HAS a menu). The references are going from the leaf nodes to the parent, instead of the parent containing a collection of references to the items it contains.

The other thing that I find unusual about what you have posted is that all the members are private. You can expose properties to the outside world, and at some point you are going to need a way to view and present the data contained within a Menu to the user.

I would rewrite the classes to look more like this:

public class Menu { public Menu() { } public Menu(long id, string name) { Id = id; Name = name; } public long Id { get; } public string Name { get; set; } public List<Category> Categories { get; } = new List<Category>(); } 

Note that I've used a get for some, and a get/set for others. This is just an assumption, but you don't normally want the id of objects to change. Likewise, initializing the list and keeping it get-only means that users don't have to do a null check every time they work with it.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, I see what you mean about letting a item belong to the superclass. Would I then implement this futher down too, so a Category would have a list of subcategories etc?
Just had another thought about this, if the Categories live in the Menu object, do I take the Menu out of the constructor of the Category class? Doesn't that then mean I can create a Category even if a Menu doesnt exist?
Yes, and yes. The child entity types don't need to know about their parents in order to exist. I would definitely remove the Menu from the Category class, the Category from the Subcategory class, and the Subcategory from the FoodItem class. It will solve your question of 'how do I create a FoodItem without needing to create a Menu, Category, and SubCategory first?'
What if I wanted the opposite to be true? So I can only create a fooditem if a menu, category and subcategory exist?
I think you'd want a separate model for that. There is sometimes a difference between the way we structure data. For example, when you are presenting a menu to the user, you would probably want the hierarchy that I have described. However, when a user is adding an item to a menu, it would be presented differently - you need them to choose the menu, category and subcategory that the item will be added to, and then provide the details of the new food item. I would say this calls for a different model
|
1

Adding to the other answer.

A category doesn't have a parent category, but a sub-category does. But otherwise they are very similar. So I would consider defining a sub-category as a sub-type of category. Making this a recursive data structure that allows for sub-...sub-sub-categories.

public class SubCategory : Category { public SubCategory(Category Parent, ...) : base(...){ ParentCategory = Parent; } public Category ParentCategory { get; } } 

Though you could also eliminate the SubCategory class, by allowing a Category to have a null Parent.

Then a food item has a Category, which could be an instance of SubCategory. Since each menu has different food items, but may have the same categories. You should model that as a list of food items, grouped by category. Perhaps;

public class Menu { public List<FoodItem> FoodItems { get; } = new List<FoodItem>(); public IGrouping<Category,FoodItems> GroupItems => FoodItems.GroupBy(f => f.Category); } 

2 Comments

Thanks for the answer! very helpful. Im wondering, would i then need to reference within subcategory a list of food items?
Menu <=> food item should be many-many. I assume Food item => Category is 1-many. So a collection navigation property is fine.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.