Object-Oriented Design for a Restaurant Management System
Problem
Design an object-oriented system for a multi-branch restaurant to manage menus, table layouts, reservations, orders (meals per seat), billing, and notifications for upcoming reservations and status changes.
Solution
1. Requirements Analysis
Functional Requirements:
- Manage multiple restaurant branches each with its own menu and seating layouts.
- Maintain hierarchical menu: sections and items with pricing updates.
- Allow receptionists to search availability and create/cancel reservations; support check-in.
- Track free vs reserved vs occupied tables and seats; support multiple seating arrangements.
- Allow waiters to create/update orders and add meals composed of meal items per seat.
- Support chefs viewing and working on orders (order status progression).
- Generate bills/checks and process payments (cash, card, check) and record payment status.
- Send notifications for upcoming reservations or cancellations.
Non-Functional (implicit):
- Consistent state transitions (e.g., reservation status flow, order status lifecycle).
- Extensibility for additional payment types or seat categories.
2. Use Case Diagram
Actors: Receptionist, Waiter, Manager, Chef, Cashier, System, Customer.
Primary Use Cases: Add/Modify Tables, Search Tables, Create Reservation, Cancel Reservation, Check-In, Place Order, Update Order, Make Payment, Send Notification.
graph TD subgraph RMS[Restaurant Management System] UC_Add(Add/Modify Tables) UC_Search(Search Tables) UC_Reserve(Create Reservation) UC_Cancel(Cancel Reservation) UC_CheckIn(Check-In) UC_Order(Place/Update Order) UC_Pay(Make Payment) UC_Notify(Send Notification) end Receptionist --> UC_Add Receptionist --> UC_Search Receptionist --> UC_Reserve Receptionist --> UC_Cancel Receptionist --> UC_CheckIn Waiter --> UC_Order Chef --> UC_Order Cashier --> UC_Pay System --> UC_Notify Manager --> UC_Add Customer --> UC_Reserve Customer --> UC_Pay
3. Class Diagram
Core Classes & Responsibilities:
- Restaurant: Aggregate of Branches; high-level add/remove branch operations.
- Branch: Holds Menu(s), TableCharts, and localized staff.
- Menu / MenuSection / MenuItem: Hierarchical menu representation with pricing.
- Table / TableSeat: Physical seating; capacity tracking and availability queries.
- Reservation: Booking details, status transitions, associated tables, notifications.
- Order / Meal / MealItem / Check: Captures ordered items per seat and billing artifacts.
- Account / Person / Employee (Receptionist, Waiter, Manager, Chef, Cashier): Actor identities and roles.
- Notification: Reminder or status update dispatching (skeleton only).
classDiagram class Restaurant { +name: String +branches: List<Branch> +addBranch(branch) } class Branch { +name: String +location: Address +menus: List<Menu> +tableCharts: List<TableChart> } class Menu { +id: int +title: String +sections: List<MenuSection> +addSection(section) } class MenuSection { +id: int +title: String +items: List<MenuItem> +addItem(item) } class MenuItem { +id: int +title: String +price: double +updatePrice(p) } class Table { +id: int +maxCapacity: int +status: TableStatus +seats: List<TableSeat> +isFree() } class TableSeat { +number: int +type: SeatType +updateType(type) } class Reservation { +id: int +peopleCount: int +status: ReservationStatus +tables: List<Table> +updatePeopleCount(c) } class Order { +id: int +status: OrderStatus +meals: List<Meal> +addMeal(meal) } class Meal { +id: int +seat: TableSeat +items: List<MealItem> +addMealItem(item) } class MealItem { +id: int +quantity: int +menuItem: MenuItem +updateQuantity(q) } class Check { +total(): double } class Notification { +message: String +send() } class Account { +id: String +status: AccountStatus +resetPassword() } class Employee { +employeeId: String } Restaurant "1" -- "*" Branch : has Branch "1" -- "*" Menu : owns Menu "1" -- "*" MenuSection : contains MenuSection "1" -- "*" MenuItem : lists Branch "1" -- "*" TableChart : layouts Table "1" -- "*" TableSeat : contains Reservation "1" -- "*" Table : reserves Order "1" -- "*" Meal : aggregates Meal "1" -- "*" MealItem : consists MealItem "*" -- "1" MenuItem : references
4. Activity Diagrams
Activity: Place Order
graph TD A[Waiter selects table] --> B{Table free?} B -- Yes --> C[Create Order] B -- No --> D[Show error] C --> E[Add Meals per Seat] E --> F[Submit Order] F --> G[Chef Prepares]
Activity: Make Reservation
graph TD R1[Receptionist searches tables] --> R2{Available?} R2 -- Yes --> R3[Collect Customer Details] R2 -- No --> R4[Suggest Alternate] R3 --> R5[Create Reservation] R5 --> R6[Send Confirmation Notification]
Activity: Cancel Reservation
graph TD C1[Customer/Receptionist selects reservation] --> C2{Within cancel window?} C2 -- Yes --> C3[Update Status to CANCELED] C2 -- No --> C4[Mark ABANDONED or Reject] C3 --> C5[Release Tables] C5 --> C6[Send Cancellation Notification]
5. High-Level Code Implementation
Java
enum ReservationStatus { REQUESTED, PENDING, CONFIRMED, CHECKED_IN, CANCELED, ABANDONED }
enum SeatType { REGULAR, KID, ACCESSIBLE, OTHER }
enum OrderStatus { RECEIVED, PREPARING, COMPLETED, CANCELED }
enum TableStatus { FREE, RESERVED, OCCUPIED, OTHER }
enum AccountStatus { ACTIVE, CLOSED, CANCELED, BLACKLISTED, BLOCKED }
class Address {
private String street; private String city; private String state; private String zip; private String country;
Address(String street, String city, String state, String zip, String country) {
this.street = street; this.city = city; this.state = state; this.zip = zip; this.country = country;
}
}
class Account {
private String id; private String password; private AccountStatus status; private Address address;
Account(String id, String password, Address address) { this.id=id; this.password=password; this.address=address; this.status=AccountStatus.ACTIVE; }
void resetPassword() { /* ... */ }
}
abstract class Person { protected String name; protected String email; protected String phone; }
abstract class Employee extends Person { protected String employeeId; protected Account account; Employee(String id, Account acc, String name, String email, String phone){ this.employeeId=id; this.account=acc; this.name=name; this.email=email; this.phone=phone; }}
class Receptionist extends Employee { Receptionist(String id, Account acc, String n, String e, String p){ super(id,acc,n,e,p);} void createReservation(){ } }
class Manager extends Employee { Manager(String id, Account acc, String n, String e, String p){ super(id,acc,n,e,p);} void addEmployee(Employee emp){ } }
class Chef extends Employee { Chef(String id, Account acc, String n, String e, String p){ super(id,acc,n,e,p);} void takeOrder(Order order){ } }
class MenuItem { int id; String title; String description; double price; void updatePrice(double p){ this.price = p; } }
class MenuSection { int id; String title; java.util.List<MenuItem> items = new java.util.ArrayList<>(); void addItem(MenuItem mi){ items.add(mi);} }
class Menu { int id; String title; java.util.List<MenuSection> sections = new java.util.ArrayList<>(); void addSection(MenuSection s){ sections.add(s);} }
class TableSeat { int number; SeatType type = SeatType.REGULAR; void updateType(SeatType t){ this.type = t; } }
class Table { int id; int maxCapacity; TableStatus status = TableStatus.FREE; java.util.List<TableSeat> seats = new java.util.ArrayList<>(); boolean isFree(){ return status==TableStatus.FREE; } }
class MealItem { int id; int quantity; MenuItem menuItem; void updateQuantity(int q){ this.quantity=q; } }
class Meal { int id; TableSeat seat; java.util.List<MealItem> items = new java.util.ArrayList<>(); void addMealItem(MealItem mi){ items.add(mi);} }
class Check { double total(){ return 0.0; } }
class Order { int id; OrderStatus status; java.util.List<Meal> meals = new java.util.ArrayList<>(); void addMeal(Meal m){ meals.add(m);} }
class Reservation { int id; int peopleCount; ReservationStatus status = ReservationStatus.REQUESTED; java.util.List<Table> tables = new java.util.ArrayList<>(); void updatePeopleCount(int c){ peopleCount=c; } }
class Notification { String message; void send(){ } }
class Restaurant { String name; java.util.List<Branch> branches = new java.util.ArrayList<>(); void addBranch(Branch b){ branches.add(b);} }
class Branch { String name; Address location; java.util.List<Menu> menus = new java.util.ArrayList<>(); java.util.List<Table> tables = new java.util.ArrayList<>(); }
class TableChart { int id; java.util.List<Table> snapshot = new java.util.ArrayList<>(); }
Python
from enum import Enum
from typing import List
class ReservationStatus(Enum):
REQUESTED = 1; PENDING = 2; CONFIRMED = 3; CHECKED_IN = 4; CANCELED = 5; ABANDONED = 6
class SeatType(Enum):
REGULAR = 1; KID = 2; ACCESSIBLE = 3; OTHER = 4
class OrderStatus(Enum):
RECEIVED = 1; PREPARING = 2; COMPLETED = 3; CANCELED = 4
class TableStatus(Enum):
FREE = 1; RESERVED = 2; OCCUPIED = 3; OTHER = 4
class AccountStatus(Enum):
ACTIVE = 1; CLOSED = 2; CANCELED = 3; BLACKLISTED = 4; BLOCKED = 5
class Address:
def __init__(self, street: str, city: str, state: str, zip_code: str, country: str) -> None:
self.street = street; self.city = city; self.state = state; self.zip_code = zip_code; self.country = country
class Account:
def __init__(self, id: str, password: str, address: Address) -> None:
self.id = id; self.password = password; self.address = address; self.status = AccountStatus.ACTIVE
def reset_password(self) -> None:
pass
class Person:
def __init__(self, name: str, email: str, phone: str) -> None:
self.name = name; self.email = email; self.phone = phone
class Employee(Person):
def __init__(self, employee_id: str, account: Account, name: str, email: str, phone: str) -> None:
super().__init__(name, email, phone); self.employee_id = employee_id; self.account = account
class Receptionist(Employee):
def create_reservation(self) -> None: pass
class Manager(Employee):
def add_employee(self) -> None: pass
class Chef(Employee):
def take_order(self) -> None: pass
class MenuItem:
def __init__(self, id: int, title: str, description: str, price: float) -> None:
self.id = id; self.title = title; self.description = description; self.price = price
def update_price(self, price: float) -> None:
self.price = price
class MenuSection:
def __init__(self, id: int, title: str, description: str) -> None:
self.id = id; self.title = title; self.description = description; self.items: List[MenuItem] = []
def add_menu_item(self, item: MenuItem) -> None:
self.items.append(item)
class Menu:
def __init__(self, id: int, title: str, description: str) -> None:
self.id = id; self.title = title; self.description = description; self.sections: List[MenuSection] = []
def add_menu_section(self, section: MenuSection) -> None:
self.sections.append(section)
class TableSeat:
def __init__(self, number: int, seat_type: SeatType = SeatType.REGULAR) -> None:
self.number = number; self.seat_type = seat_type
def update_seat_type(self, seat_type: SeatType) -> None:
self.seat_type = seat_type
class Table:
def __init__(self, id: int, max_capacity: int) -> None:
self.id = id; self.max_capacity = max_capacity; self.status = TableStatus.FREE; self.seats: List[TableSeat] = []
def is_free(self) -> bool: return self.status == TableStatus.FREE
class MealItem:
def __init__(self, id: int, quantity: int, menu_item: MenuItem) -> None:
self.id = id; self.quantity = quantity; self.menu_item = menu_item
def update_quantity(self, quantity: int) -> None:
self.quantity = quantity
class Meal:
def __init__(self, id: int, seat: TableSeat) -> None:
self.id = id; self.seat = seat; self.items: List[MealItem] = []
def add_meal_item(self, item: MealItem) -> None:
self.items.append(item)
class Check:
def __init__(self) -> None: pass
def total(self) -> float: return 0.0
class Order:
def __init__(self, id: int, status: OrderStatus, table: Table, waiter: Receptionist, chef: Chef) -> None:
self.id = id; self.status = status; self.meals: List[Meal] = []; self.table = table; self.waiter = waiter; self.chef = chef; self.check = Check()
def add_meal(self, meal: Meal) -> None:
self.meals.append(meal)
class Reservation:
def __init__(self, id: int, people_count: int) -> None:
self.id = id; self.people_count = people_count; self.status = ReservationStatus.REQUESTED; self.tables: List[Table] = []
def update_people_count(self, count: int) -> None:
self.people_count = count
class Notification:
def __init__(self, message: str) -> None:
self.message = message
def send(self) -> None: pass
class Restaurant:
def __init__(self, name: str) -> None:
self.name = name; self.branches: List[Branch] = []
def add_branch(self, branch: 'Branch') -> None:
self.branches.append(branch)
class Branch:
def __init__(self, name: str, location: Address) -> None:
self.name = name; self.location = location; self.menus: List[Menu] = []; self.tables: List[Table] = []
class TableChart:
def __init__(self, id: int) -> None:
self.id = id; self.snapshot: List[Table] = []
Enums, data types, and constants: Here are the required enums, data types, and constants:
from enum import Enum
class ReservationStatus(Enum):
REQUESTED, PENDING, CONFIRMED, CHECKED_IN, CANCELED, ABANDONED = 1, 2, 3, 4, 5, 6
class SeatType(Enum):
REGULAR, KID, ACCESSIBLE, OTHER = 1, 2, 3, 4
class OrderStatus(Enum):
RECEIVED, PREPARING, COMPLETED, CANCELED, NONE = 1, 2, 3, 4, 5
class TableStatus(Enum):
FREE, RESERVED, OCCUPIED, OTHER = 1, 2, 3, 4
class AccountStatus(Enum):
ACTIVE, CLOSED, CANCELED, BLACKLISTED, BLOCKED = 1, 2, 3, 4, 5
class PaymentStatus(Enum):
UNPAID, PENDING, COMPLETED, FILLED, DECLINED, CANCELLED, ABANDONED, SETTLING, SETTLED, REFUNDED = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
class Address:
def __init__(self, street, city, state, zip_code, country):
self.__street_address = street
self.__city = city
self.__state = state
self.__zip_code = zip_code
self.__country = country
Account, Person, Employee, Receptionist, Manager, and Chef: These classes represent the different people that interact with our system:
from abc import ABC
from datetime import datetime
from .constants import *
# For simplicity, we are not defining getter and setter functions. The reader can
# assume that all class attributes are private and accessed through their respective
# public getter methods and modified only through their public methods function.
class Account:
def __init__(self, id, password, address, status=AccountStatus.Active):
self.__id = id
self.__password = password
self.__address = address
self.__status = status
def reset_password(self):
None
class Person(ABC):
def __init__(self, name, email, phone):
self.__name = name
self.__email = email
self.__phone = phone
class Employee(ABC, Person):
def __init__(self, id, account, name, email, phone):
super().__init__(name, email, phone)
self.__employee_id = id
self.__date_joined = datetime.date.today()
self.__account = account
class Receptionist(Employee):
def __init__(self, id, account, name, email, phone):
super().__init__(id, account, name, email, phone)
def create_reservation(self):
None
def search_customer(self, name):
None
class Manager(Employee):
def __init__(self, id, account, name, email, phone):
super().__init__(id, account, name, email, phone)
def add_employee(self):
None
class Chef(Employee):
def __init__(self, id, account, name, email, phone):
super().__init__(id, account, name, email, phone)
def take_order(self):
None
Restaurant, Branch, Kitchen, TableChart: These classes represent the top-level classes of the system
class Kitchen:
def __init__(self, name):
self.__name = name
self.__chefs = []
def assign_chef(self, chef):
None
class Branch:
def __init__(self, name, location, kitchen):
self.__name = name
self.__location = location
self.__kitchen = kitchen
def add_table_chart(self):
None
class Restaurant:
def __init__(self, name):
self.__name = name
self.__branches = []
def add_branch(self, branch):
None
class TableChart:
def __init__(self, id):
self.__table_chart_id = id
self.__table_chart_image = []
def print(self):
None
Table, TableSeat, and Reservation: Each table can have multiple seats and customers can make reservations for tables:
from datetime import datetime
from .constants import *
class Table:
def __init__(self, id, max_capacity, location_identifier, status=TableStatus.FREE):
self.__table_id = id
self.__max_capacity = max_capacity
self.__location_identifier = location_identifier
self.__status = status
self.__seats = []
def is_table_free(self):
None
def add_reservation(self):
None
def search(self, capacity, start_time):
# return all tables with the given capacity and availability
None
class TableSeat:
def __init__(self):
self.__table_seat_number = 0
self.__type = SeatType.REGULAR
def update_seat_type(self, seat_type):
None
class Reservation:
def __init__(self, id, people_count, notes, customer):
self.__reservation_id = id
self.__time_of_reservation = datetime.now()
self.__people_count = people_count
self.__status = ReservationStatus.REQUESTED
self.__notes = notes
self.__checkin_time = datetime.now()
self.__customer = customer
self.__tables = []
self.__notifications = []
def update_people_count(self, count):
None
Menu, MenuSection, and MenuItem: Each restaurant branch will have its own menu, each menu will have multiple menu sections, which will contain menu items:
class MenuItem:
def __init__(self, id, title, description, price):
self.__menu_item_id = id
self.__title = title
self.__description = description
self.__price = price
def update_price(self, price):
None
class MenuSection:
def __init__(self, id, title, description):
self.__menu_section_id = id
self.__title = title
self.__description = description
self.__menu_items = []
def add_menu_item(self, menu_item):
None
class Menu:
def __init__(self, id, title, description):
self.__menu_id = id
self.__title = title
self.__description = description
self.__menu_sections = []
def add_menu_section(self, menu_section):
None
def print(self):
None
Order, Meal, and MealItem: Each order will have meals for table seats:
from datetime import datetime
class MealItem:
def __init__(self, id, quantity, menu_item):
self.__meal_item_id = id
self.__quantity = quantity
self.__menu_item = menu_item
def update_quantity(self, quantity):
None
class Meal:
def __init__(self, id, seat):
self.__meal_id = id
self.__seat = seat
self.__menu_items = []
def add_meal_item(self, meal_item):
None
class Check():
def __init__(self):
None
class Order:
def __init__(self, id, status, table, waiter, chef):
self.__order_id = id
self.__OrderStatus = status
self.__creation_time = datetime.now()
self.__meals = []
self.__table = table
self.__waiter = waiter
self.__chef = chef
self.__check = Check()
def add_meal(self, meal):
None
def remove_meal(self, meal):
None
def get_status(self):
return self.__OrderStatus
def set_status(self, status):
None
Source https://github.com/tssovi/grokking-the-object-oriented-design-interview