π Restaurant Menu System
Master inheritance through a realistic restaurant menu. Learn to model real-world relationships, override methods, apply the Liskov Substitution Principle, and build polymorphic systems.
π― By the end of this module, you will be able to:
- Design a base
MenuItemclass with shared attributes and methods - Create specialized subclasses:
Appetizer,MainCourse,Dessert,Beverage - Override methods like
get_price()andprepare()for unique behavior - Use
super()correctly to initialize parent classes - Apply polymorphism to build flexible menu systems
- Understand the Liskov Substitution Principle (LSP)
- Identify when inheritance is (and isn't) the right tool
- Contrast inheritance with composition using real examples
π Learning Progress
The Duplication Problem
Without inheritance, you'd write nearly identical classes for each menu category:
Without Inheritance β Duplicated Code
β οΈ The Problem: The __init__ and get_price() logic is duplicated. If you need to add a new attribute (like calories), you must update it in three places. This violates the DRY (Don't Repeat Yourself) principle.
The Solution: Inheritance
With Inheritance β Clean, DRY Code
β Result: The duplication is gone. All menu items share the same base structure. If you add a new common attribute, you add it once in the base class.
π Key Concept: The Open/Closed Principle
The MenuItem base class is open for extension (you can add new menu categories) but closed for modification (you don't need to change existing code). This is one of the SOLID principles of object-oriented design.
The Liskov Substitution Principle (LSP)
LSP states that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
β Breaking LSP β Bad Example:
If code expects MainCourse and calls get_price(), it will fail unexpectedly for this subclass. This violates LSP.
β Following LSP β Good Example:
This subclass extends behavior without breaking the contract. It still returns a float and works wherever MainCourse is expected.
Inheritance vs Composition
A ComboMeal is not a type of MenuItem β it has menu items. This is composition, and it's often the better choice.
Inheritance ("is-a")
"A MainCourse is a MenuItem"
- Use for specialization
- Changes to parent affect children
- Can lead to deep hierarchies
Composition ("has-a")
"A ComboMeal has MenuItem objects"
- Use for containment
- Changes are isolated
- More flexible and testable
π‘ When to use which: Use inheritance when you have a clear "is-a" relationship and you want to reuse behavior. Use composition when you have a "has-a" relationship or when you want to avoid tight coupling. In modern Python, many developers prefer composition + protocols over deep inheritance.
Build the Restaurant Menu
Edit the code below, then click Run to execute it. Click Test to run automated tests on your code.
π‘ Try it yourself: Add a Beverage class, change pricing rules, or add a new method. Click Test to run automated tests on your changes.
Level Up Your Skills
Complete these exercises in order. Each one builds on the previous concepts.
π§ͺ Exercise 1: Add a Beverage Category Easy Not Started
Create a Beverage class that inherits from MenuItem.
- A
sizeattribute (small, medium, large, extra large) - An
is_hotboolean attribute - A
prepare()method that returns a preparation message - A
display()method that shows the size and temperature - Override
get_price()to add $0.50 for large and $1.00 for extra large
MenuItem. Don't forget super().__init__()!get_price(), check self.size and add the appropriate amount.β for hot and π§ for iced in display().π§ͺ Exercise 2: Debug the Menu Medium Not Started
This code has a bug that causes an AttributeError. Find and fix it:
__init__. What does super() do?name, price, and description.super().__init__(name, price, description) as the first line of __init__.π§ͺ Exercise 3: Combo Meal (Composition) Hard Not Started
Create a ComboMeal class that contains multiple MenuItem objects and calculates a discounted total.
This exercise demonstrates composition ("has-a" relationship) rather than inheritance.
sum() with a generator to calculate the total.get_price() method should apply the discount to the total.π§ͺ Exercise 4: Fix the LSP Violation Hard Not Started
This class violates the Liskov Substitution Principle. Fix it so it works correctly.
super().get_price() + 1.50 for spicy dishes.π§ͺ Exercise 5: Method Resolution Order (MRO) Medium Not Started
What will this code print? Try to work it out, then click "Run" to check.
Test Your Understanding
Question 1: What does super().__init__() do?
Question 2: Why does the base MenuItem raise NotImplementedError?
Question 3: What concept is demonstrated by calculate_total() working with any menu item?
Question 4: What is the Liskov Substitution Principle?
Question 5: When should you prefer composition over inheritance?
Question 6: What does ClassName.__mro__ show?
What to Avoid
| Mistake | Why It's Bad | Better Approach |
|---|---|---|
Forgetting super().__init__() |
Parent attributes are never initialized | Always call super().__init__() first |
| Deep inheritance (5+ levels) | Hard to maintain, fragile | Prefer shallow hierarchies + composition |
| Raising exceptions in overridden methods | Breaks LSP, violates the contract | Extend behavior, don't restrict it |
| Base class knows about subclasses | Violates Open/Closed Principle | Keep base class generic |
| Overriding to change a tiny detail | Adds unnecessary complexity | Use parameters or composition |
| Mutable defaults in __init__ | Shared state across instances | Use None and create fresh objects |