I have one base-class named Product. Classes "Drink" and "Pizza" are subclasses that inherit from the "Product" class. I also have a class named "Ingredient" Which is a part of the Pizza class, so the Pizza class should have an instance (a list) of the Ingredient class.
My question is: Since the Ingredient has the same properties like all the other "Products", can it also inherit from the "Product" class while working together with the "Pizza" subclass?
Yes, classes that inherit from the same super-class can perfectly work together. There is no incompatibility. There is even a design pattern that uses both relations on the same classes (the composite pattern).
Remark: The wording "Working together" is somewhat ambiguous and could mean different things. But your class-diagram is precise enough and shows that you mean a possible association.
Your diagram shows a navigable association between Pizza
and Ingredient
(the open arrow). This means that the implementation shall make sure that a pizza can easily find its ingredients.
In your narrative you mention a list. I understand that an Ingredient
can exist without any Pizza, but a Pizza can have several Ingredient
. It is important to specify this multiplicity on the diagram, by indicating *
on the Ingredient
side of the association.
The relationship in the opposite direction has still some mysteries:
Ingredient
shall be able to easily find the related pizza or if this is not relevant. You may specify this further with an arrow-head in the opposite direction (navigable) or with an X across the line (non-navigable). You have the right to leave this unspecified and decide later. The navigability has an impact on the way the classes can work together: it means that Pizza
can "work with" ingredient (e.g. use it as parameter in an operation, invoke an ingredient operation directly, etc...). But the absence of navigability in the opposite direction would mean that Ingredient
could not by itself initiate a collaboration with its pizza.0..1
), or if it can be shared between several (0..*
). This is very important to know because the implementation would be very different (in the latter case you'd have a many-to-many association).Inheritance is tempting when one discovers OOP. However, inheritance has a lot of implications, constraints and consequences. Therefore, use it wisely.
A useful advice is to start considering that A inherits from B, only if A is a more specialized B, or conversely, that B is a more generalization of A. It is dangerous to use inheritance just because some properties or operations share the same name. Names can by the way be misleading.
In your case, I understand a Product
as something that the company sells at a given price: Drink
, Pizza
, maybe Antipasti
or Pasta
. The price indicated, is the price requested from the customer. I therefore wonder if an Ingredient
is sold to the customer. AN ingredient may have a price, but it is a purchase price. A purchase price is different from a sales price: imagine that a restaurant would subcontract some very special pizza: the sales price would be different from the purchasing price.
Of course, if your Product
is something more general, in the business meaning, and if it can have a sales price and a purchase price, and is not necessarily on the menu, then no problem, go-ahead. But be aware that such generalized products are much more complex to manage (e.g. here a well-known ERP example with more than 20 different screens to manage all the aspects of a product).
A very common advice is to prefer composition over inheritance. This rule of thumb aims to remind us to think twice before we use inheritance. Let's be clear, in case of doubt: this does not mean that inheritance and composition are incompatible.