A class diagram is a type of structural diagram in the Unified Modeling Language (UML) that represents the static structure of a system. It shows classes, their attributes, methods, and the relationships between them. Class diagrams are essential for documenting object-oriented (OOP) systems and serve as a bridge between design and implementation.
Class diagrams are the most widely used UML diagram type in software development. They answer fundamental questions:
Unlike sequence or activity diagrams that show behavior over time, class diagrams provide a static snapshot of the system's architecture. They are invaluable for:
A class in a class diagram is represented as a rectangle divided into three horizontal sections:
Example class structure:
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Class Box Anatomy — Customer │ │ │ │ ┌────────────────────────────────┐ │ │ │ Customer │ │ │ ├────────────────────────────────┤ │ │ │ -customerId: int │ │ │ │ -name: String │ │ │ │ -email: String │ │ │ │ #membershipLevel: int │ │ │ │ +isActive: boolean │ │ │ ├────────────────────────────────┤ │ │ │ +getOrders(): List<Order> │ │ │ │ -calculateDiscount(): float │ │ │ │ +updateEmail(newEmail) │ │ │ └────────────────────────────────┘ │ │ │ │ Top: name · Middle: attributes · Bottom: methods │ │ + public · - private · # protected · ~ package │ └────────────────────────────────────────────────────────────────────────────────────────┘
Visibility determines which classes can access an attribute or method:
Abstract classes are displayed with italicized names and cannot be instantiated directly. They serve as base classes for concrete implementations.
Interfaces are marked with the <<interface>> stereotype. They define
contracts that other classes must implement.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Abstract Class & Interface Notation │ │ │ │ ┌──────────────────────────┐ ┌──────────────────────────┐ │ │ │ <<interface>> │ │ *PaymentMethod* │ │ │ │ Drawable │ ├──────────────────────────┤ │ │ ├──────────────────────────┤ │ #amount: float │ │ │ │ │ ├──────────────────────────┤ │ │ ├──────────────────────────┤ │ +processPayment() │ │ │ │ +draw(): void │ │ +refund(): boolean │ │ │ │ +getArea(): float │ └──────────────────────────┘ │ │ └──────────────────────────┘ │ │ │ │ Interface: <<interface>> stereotype · Abstract: *italicized name* │ └────────────────────────────────────────────────────────────────────────────────────────┘
Enumerations (enums) are shown as classes with the <<enumeration>> stereotype, listing
constant values instead of methods:
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Enumeration │ │ │ │ ┌──────────────────────────┐ │ │ │ <<enumeration>> │ │ │ │ OrderStatus │ │ │ ├──────────────────────────┤ │ │ │ PENDING │ │ │ │ PROCESSING │ │ │ │ SHIPPED │ │ │ │ DELIVERED │ │ │ │ CANCELLED │ │ │ ├──────────────────────────┤ │ │ │ │ │ │ └──────────────────────────┘ │ │ │ │ <<enumeration>> stereotype lists constant values │ └────────────────────────────────────────────────────────────────────────────────────────┘
Class diagrams show five primary types of relationships. Each is represented by different line styles and symbols.
An association represents a structural relationship between two independent classes. It indicates that instances of one class are somehow related to instances of another. Associations are the weakest form of coupling.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Association — Solid Line + Multiplicity │ │ │ │ 1 teaches * │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ Teacher │ │ Student │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -name: str │─────────────────────────────▶│ -name: str │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ +teach() │ │ +study() │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ Multiplicity: 1 = exactly one · * = zero or more · 1..* = one or more │ └────────────────────────────────────────────────────────────────────────────────────────┘
The numbers 1 and * represent multiplicity:
1 → Exactly one0..1 → Zero or one (optional)* → Zero or more (unbounded)1..* → One or more2..5 → Between two and fiveAn aggregation is a "has-a" relationship where the whole and part are loosely coupled. The part can exist independently of the whole. The diamond symbol appears on the side of the whole class.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Aggregation — Empty Diamond (Has-A, Independent Lifecycle) │ │ │ │ 1 employs 1..* │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ Department │ │ Employee │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -name: str │ │ -name: str │ │ │ │ -budget │◇────────────────────────────▶│ -salary │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ +hire() │ │ +work() │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ Department HAS Employees · Employees can exist without the Department │ └────────────────────────────────────────────────────────────────────────────────────────┘
A composition is a strong "has-a" relationship where the whole owns the part. The part cannot exist without the whole. When the whole is deleted, parts are also deleted. The filled diamond indicates ownership.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Composition — Filled Diamond (Owned Lifecycle) │ │ │ │ 1 contains 1..* │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ House │ │ Room │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -address │ │ -name │ │ │ │ -owner │◆────────────────────────────▶│ -area │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ +renovate() │ │ +clean() │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ House is COMPOSED OF Rooms · Rooms cannot exist without the House │ └────────────────────────────────────────────────────────────────────────────────────────┘
Inheritance (also called generalization) represents an "is-a" relationship. A subclass inherits attributes and methods from a superclass. The arrow points from the subclass to the superclass.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Inheritance / Generalization — Animal Hierarchy │ │ │ │ ┌──────────────────────────┐ │ │ │ *Animal* │ │ │ ├──────────────────────────┤ │ │ │ #name: str │ │ │ │ #age: int │ │ │ ├──────────────────────────┤ │ │ │ +eat(): void │ │ │ │ +move(): void │ │ │ └──────────────────────────┘ │ │ │ │ │ │ │ │ ┌───────────────────────┴───────────────────────┐ │ │ △ △ △ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Dog │ │ Cat │ │ Bird │ │ │ ├──────────────┤ ├──────────────┤ ├──────────────┤ │ │ │ │ │ │ │ │ │ │ ├──────────────┤ ├──────────────┤ ├──────────────┤ │ │ │ +bark(): void│ │ +meow(): void│ │ +fly(): void │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ Inheritance arrow: empty triangle △ pointing TO the parent class │ └────────────────────────────────────────────────────────────────────────────────────────┘
Realization shows that a class implements an interface. The dashed line arrow points from the implementing class to the interface.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Realization — Interface Implementation (Dashed + Triangle) │ │ │ │ ┌──────────────────────────────┐ │ │ │ <<interface>> │ │ │ │ Comparable │ │ │ ├──────────────────────────────┤ │ │ │ │ │ │ ├──────────────────────────────┤ │ │ │ +compareTo(other) │ │ │ └──────────────────────────────┘ │ │ ╎ │ │ ╎ │ │ △ │ │ ┌──────────────────────────────┐ │ │ │ Person │ │ │ ├──────────────────────────────┤ │ │ │ -name: str │ │ │ │ -age: int │ │ │ ├──────────────────────────────┤ │ │ │ +compareTo(other) │ │ │ └──────────────────────────────┘ │ │ │ │ Person realizes the Comparable interface (dashed line, empty triangle) │ └────────────────────────────────────────────────────────────────────────────────────────┘
A dependency is a weak relationship where one class uses or depends upon another class, but they are not structurally connected. It typically represents a method parameter, local variable, or return type.
┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Dependency — Dashed Arrow (Weak Coupling) │ │ │ │ uses │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ Report │ │ Database │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -title: str │ │ │ │ │ ├────────────────────┤╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶├────────────────────┤ │ │ │ +generate() │ │ +query() │ │ │ │ -fetchData() │ │ +save() │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ Report depends on Database (parameter, local var, or return type) │ └────────────────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ E-Commerce Class Diagram │ │ │ │ 1 │ │ ┌────────────────────────────┐ ┌──────────────────────────────┐ │ │ │ *ShippingAddress* │ │ Customer │ │ │ ├────────────────────────────┤ ├──────────────────────────────┤ │ │ │ -street: str │ │ -customerId: int │ │ │ │ -city: str │ │ -name: str │ │ │ │ -zip: str │◆──────────────▶│ -email: str │ │ │ │ -country: str │ │ #level: int │ │ │ ├────────────────────────────┤ ├──────────────────────────────┤ │ │ │ +isValid(): bool │ │ +getOrders() │ │ │ └────────────────────────────┘ │ +calculateDiscount() │ │ │ └──────────────────────────────┘ │ │ │ │ │ 1..* (places) │ │ ▼ │ │ ┌────────────────────────────┐ │ │ │ Order │ │ │ ├────────────────────────────┤ │ │ │ -orderId: int │ │ │ │ -date: datetime │ │ │ │ -total: float │ │ │ ├────────────────────────────┤ │ │ │ +calculate() │ │ │ │ +apply_discount() │ │ │ └────────────────────────────┘ │ │ │ 1..* (contains) │ │ ▼ │ │ ┌────────────────────────────┐ │ │ │ OrderItem │ │ │ ├────────────────────────────┤ │ │ │ -itemId: int │ │ │ │ -quantity: int │ │ │ │ -price: float │ │ │ ├────────────────────────────┤ │ │ │ +getSubtotal() │ │ │ └────────────────────────────┘ │ │ │ * → 1 (references) │ │ ▼ │ │ ┌────────────────────────────┐ │ │ │ Product │ │ │ ├────────────────────────────┤ │ │ │ -productId: int │ │ │ │ -name: str │ │ │ │ -price: float │ │ │ │ -stock: int │ │ │ ├────────────────────────────┤ │ │ │ +updateStock() │ │ │ │ +isAvailable() │ │ │ └────────────────────────────┘ │ │ │ │ Order also uses ◇ PaymentMethod · CreditCardPayment △── PaymentMethod │ │ │ │ △ │ │ ┌────────────────────────────┐ ┌────────────────────────────┐ │ │ │ *PaymentMethod* │ │ CreditCardPayment │ │ │ ├────────────────────────────┤ ├────────────────────────────┤ │ │ │ #amount: float │ │ -cardNumber: str │ │ │ │ #status: str │╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▶├────────────────────────────┤ │ │ ├────────────────────────────┤ │ +process(): bool │ │ │ │ +process(): bool │ │ +refund(): bool │ │ │ │ +refund(): bool │ └────────────────────────────┘ │ │ └────────────────────────────┘ │ │ │ │ PaymentMethod is *abstract* (italic name) · CreditCardPayment realizes it │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime
from enum import Enum
from abc import ABC, abstractmethod
class PaymentStatus(Enum):
PENDING = "pending"
COMPLETED = "completed"
FAILED = "failed"
REFUNDED = "refunded"
@dataclass
class ShippingAddress:
"""Represents a shipping address (composition within Customer)."""
street: str
city: str
zip_code: str
country: str
def is_valid(self) -> bool:
"""Validate address format."""
return all([self.street, self.city, self.zip_code, self.country])
@dataclass
class Product:
"""Represents a product in the e-commerce system."""
product_id: int
name: str
price: float
stock: int
category: str
def update_stock(self, quantity: int) -> None:
"""Reduce stock when product is ordered."""
self.stock = max(0, self.stock - quantity)
def is_available(self, quantity: int = 1) -> bool:
"""Check if product is available in requested quantity."""
return self.stock >= quantity
class PaymentMethod(ABC):
"""Abstract base class for payment methods."""
def __init__(self, amount: float):
self.amount = amount
self.status = PaymentStatus.PENDING
@abstractmethod
def process(self) -> bool:
"""Process the payment."""
pass
@abstractmethod
def refund(self) -> bool:
"""Refund the payment."""
pass
class CreditCardPayment(PaymentMethod):
"""Concrete implementation of credit card payment."""
def __init__(self, amount: float, card_number: str):
super().__init__(amount)
self.card_number = card_number
def process(self) -> bool:
"""Process credit card payment."""
# Simulate card processing
self.status = PaymentStatus.COMPLETED
return True
def refund(self) -> bool:
"""Refund credit card payment."""
if self.status == PaymentStatus.COMPLETED:
self.status = PaymentStatus.REFUNDED
return True
return False
@dataclass
class OrderItem:
"""Represents a line item in an order."""
item_id: int
product: Product
quantity: int
price: float
def get_subtotal(self) -> float:
"""Calculate subtotal for this item."""
return self.price * self.quantity
@dataclass
class Order:
"""Represents a customer order."""
order_id: int
customer_id: int
date: datetime
items: List[OrderItem]
shipping_address: ShippingAddress
payment: Optional[PaymentMethod] = None
total: float = 0.0
def calculate(self) -> float:
"""Calculate total order amount."""
self.total = sum(item.get_subtotal() for item in self.items)
return self.total
def apply_discount(self, discount_percent: float) -> float:
"""Apply discount to order total."""
discount_amount = self.total * (discount_percent / 100)
return self.total - discount_amount
@dataclass
class Customer:
"""Represents a customer (composition with ShippingAddress)."""
customer_id: int
name: str
email: str
membership_level: int = 0
is_active: bool = True
orders: List[Order] = None
shipping_address: ShippingAddress = None
def __post_init__(self):
if self.orders is None:
self.orders = []
def get_orders(self) -> List[Order]:
"""Retrieve all orders for this customer."""
return self.orders
def calculate_discount(self) -> float:
"""Calculate loyalty discount based on membership level."""
discounts = {0: 0.0, 1: 0.05, 2: 0.10, 3: 0.15}
return discounts.get(self.membership_level, 0.0)
def place_order(self, order: Order) -> bool:
"""Place a new order for this customer."""
if self.is_active and order.shipping_address.is_valid():
self.orders.append(order)
return True
return False
# Example usage
if __name__ == "__main__":
# Create a customer with shipping address
address = ShippingAddress(
street="123 Main St",
city="Portland",
zip_code="97201",
country="USA"
)
customer = Customer(
customer_id=1,
name="Alice Johnson",
email="alice@example.com",
membership_level=2,
shipping_address=address
)
# Create products
product1 = Product(1, "Laptop", 999.99, 10, "Electronics")
product2 = Product(2, "Mouse", 29.99, 50, "Accessories")
# Create order items
item1 = OrderItem(1, product1, 1, 999.99)
item2 = OrderItem(2, product2, 2, 29.99)
# Create order
order = Order(
order_id=101,
customer_id=1,
date=datetime.now(),
items=[item1, item2],
shipping_address=address
)
# Calculate total and apply discount
total = order.calculate()
discount = customer.calculate_discount()
final_total = order.apply_discount(discount * 100)
# Process payment
payment = CreditCardPayment(final_total, "4111111111111111")
payment.process()
order.payment = payment
print(f"Order Total: ${total:.2f}")
print(f"Discount: {discount*100}%")
print(f"Final Total: ${final_total:.2f}")
print(f"Payment Status: {payment.status.value}")
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Strategy Pattern — Interchangeable Algorithms │ │ │ │ ┌──────────────────────────────┐ │ │ │ <<interface>> │ │ │ │ PaymentStrategy │ │ │ ├──────────────────────────────┤ │ │ │ │ │ │ ├──────────────────────────────┤ │ │ │ +pay(amount): bool │ │ │ └──────────────────────────────┘ │ │ ╎ │ │ ╎ │ │ ┌─────────────────────────┴─────────────────────────┐ │ │ △ △ △ │ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ │ CardPay │ │ PayPalPay │ │ BitcoinPay │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ │ │ │ │ │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ +pay() │ │ +pay() │ │ +pay() │ │ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ │ │ ┌────────────────────────────┐ │ │ │ PaymentProcessor │ │ │ ├────────────────────────────┤ │ │ │ -strategy │ │ │ ├────────────────────────────┤ │ │ │ +execute() │ │ │ └────────────────────────────┘ │ │ │ │ PaymentProcessor depends on PaymentStrategy (uses any concrete pay method) │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘
The Observer pattern defines a one-to-many dependency where when one object changes state, all dependents are notified automatically.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Observer Pattern — One-to-Many Notification │ │ │ │ 1..* │ │ ┌────────────────────────┐ ┌──────────────────────┐ │ │ │ Subject │ │ <<interface>> │ │ │ ├────────────────────────┤ │ Observer │ │ │ │ -observers: List │ ├──────────────────────┤ │ │ ├────────────────────────┤◇──────────────▶│ │ │ │ │ +attach(o) │ ├──────────────────────┤ │ │ │ +detach(o) │ │ +update(): void │ │ │ │ +notify() │ └──────────────────────┘ │ │ └────────────────────────┘ │ │ ╎ │ │ ╎ │ │ ┌─────────────────────────────┴─────────────────────────────┐ │ │ △ △ △ │ │ ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐ │ │ │ EmailSubscriber │ │ SMSSubscriber │ │ LogSubscriber │ │ │ ├────────────────────┤ ├────────────────────┤ ├────────────────────┤ │ │ │ │ │ │ │ │ │ │ ├────────────────────┤ ├────────────────────┤ ├────────────────────┤ │ │ │ +update() │ │ +update() │ │ +update() │ │ │ └────────────────────┘ └────────────────────┘ └────────────────────┘ │ │ │ │ Subject notifies all attached Observers when state changes │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘
The Factory pattern provides an interface for creating objects without specifying their concrete classes.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Factory Pattern — Object Creation Abstraction │ │ │ │ ┌────────────────────────────┐ │ │ │ *DocumentFactory* │ │ │ ├────────────────────────────┤ │ │ │ │ │ │ ├────────────────────────────┤ │ │ │ +createDocument() │ │ │ └────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────┴─────────────────────────┐ │ │ △ △ △ │ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ │ PDFFactory │ │ WordFactory │ │ ExcelFactory │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ │ │ │ │ │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ +create() │ │ +create() │ │ +create() │ │ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ (creates) │ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ │ PDFDoc │ │ WordDoc │ │ ExcelDoc │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ │ │ │ │ │ │ │ ├────────────────┤ ├────────────────┤ ├────────────────┤ │ │ │ +open() │ │ +open() │ │ +open() │ │ │ │ +save() │ │ +save() │ │ +save() │ │ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ │ │ Each Factory creates its own Document type (returned to caller) │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘
# Strategy Pattern
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: float) -> bool:
pass
class CardPayment(PaymentStrategy):
def pay(self, amount: float) -> bool:
print(f"Processing card payment of ${amount}")
return True
class PayPalPayment(PaymentStrategy):
def pay(self, amount: float) -> bool:
print(f"Processing PayPal payment of ${amount}")
return True
class PaymentProcessor:
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def execute_payment(self, amount: float) -> bool:
return self.strategy.pay(amount)
# Observer Pattern
class Observer(ABC):
@abstractmethod
def update(self, message: str) -> None:
pass
class Subject:
def __init__(self):
self._observers: list = []
def attach(self, observer: Observer) -> None:
self._observers.append(observer)
def detach(self, observer: Observer) -> None:
self._observers.remove(observer)
def notify(self, message: str) -> None:
for observer in self._observers:
observer.update(message)
class EmailSubscriber(Observer):
def update(self, message: str) -> None:
print(f"Email sent: {message}")
class SMSSubscriber(Observer):
def update(self, message: str) -> None:
print(f"SMS sent: {message}")
# Factory Pattern
class DocumentFactory(ABC):
@abstractmethod
def create_document(self):
pass
class PDFFactory(DocumentFactory):
def create_document(self):
return PDFDocument()
class WordFactory(DocumentFactory):
def create_document(self):
return WordDocument()
class PDFDocument:
def save(self, filename: str) -> None:
print(f"Saving PDF: {filename}")
class WordDocument:
def save(self, filename: str) -> None:
print(f"Saving Word: {filename}")
A typical REST API involves models for authentication, authorization, and data transfer.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ REST API Models — Response Hierarchy & Auth │ │ │ │ --- Response Hierarchy (Generalization) --- │ │ │ │ ┌──────────────────────────┐ │ │ │ *APIResponse<T>* │ │ │ ├──────────────────────────┤ │ │ │ -statusCode: int │ │ │ │ -message: str │ │ │ │ -data: T │ │ │ ├──────────────────────────┤ │ │ │ +toJson(): str │ │ │ └──────────────────────────┘ │ │ │ │ │ │ │ │ ┌───────────────┴─────────────┐ │ │ △ △ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ SuccessResponse │ │ ErrorResponse │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -timestamp │ │ -errors: List │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ │ │ │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ --- Authentication: Token, User, Role, Permission --- │ │ │ │ 1 1..* * │ │ ┌────────────────────────────┐ ┌──────────────────────────┐ │ │ │ Token │ │ User │ │ │ ├────────────────────────────┤ ├──────────────────────────┤ │ │ │ -accessToken: str │ │ -userId: int │ │ │ │ -tokenType: str │ │ -username: str │ │ │ │ -expiresIn: int │◆──────────▶│ -email: str │ │ │ ├────────────────────────────┤ ├──────────────────────────┤ │ │ │ +isValid(): bool │ │ +getRole() │ │ │ │ +refresh(): Token │ └──────────────────────────┘ │ │ └────────────────────────────┘ │ │ │ │ User has 1..* Role ──▶ Role has 1..* Permission │ │ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ Role │ │ Permission │ │ │ ├────────────────────┤ ├────────────────────┤ │ │ │ -roleId │ │ -permId │ │ │ │ -name │ │ -resource │ │ │ ├────────────────────┤ │ -action │ │ │ │ +permissions() │ ├────────────────────┤ │ │ └────────────────────┘ │ │ │ │ └────────────────────┘ │ │ │ │ Token composes (◆) User · User has many Roles · Role has many Permissions │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘
Mermaid is a JavaScript-based diagramming and charting tool that renders markdown-inspired syntax. Below is the Mermaid classDiagram syntax for the e-commerce system:
classDiagram
class Customer {
-int customerId
-string name
-string email
#int membershipLevel
+List~Order~ getOrders()
+float calculateDiscount()
}
class Order {
-int orderId
-datetime date
-float total
+float calculate()
}
class OrderItem {
-int itemId
-int quantity
-float price
+float getSubtotal()
}
class Product {
-int productId
-string name
-float price
-int stock
+void updateStock()
+bool isAvailable()
}
class PaymentMethod {
<<abstract>>
#float amount
#string status
+bool process()
+bool refund()
}
class CreditCardPayment {
-string cardNumber
+bool process()
+bool refund()
}
class ShippingAddress {
-string street
-string city
-string zipCode
-string country
+bool isValid()
}
Customer "1" --> "*" Order : places
Customer "1" -- "1" ShippingAddress : has
Order "1" --> "*" OrderItem : contains
OrderItem "*" --> "1" Product : references
Order "*" --> "1" PaymentMethod : uses
CreditCardPayment --|> PaymentMethod : implements
Class diagrams translate directly into code. Here's how the mapping works:
extends or :implements keywordfrom dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime
from enum import Enum
from abc import ABC, abstractmethod
class OrderStatus(Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
@dataclass
class ShippingAddress:
"""Represents a physical shipping address."""
street: str
city: str
zip_code: str
country: str
def is_valid(self) -> bool:
"""Validate that all required fields are present."""
return bool(self.street and self.city and self.zip_code and self.country)
def format_address(self) -> str:
"""Format address for display."""
return f"{self.street}\n{self.city}, {self.zip_code}\n{self.country}"
@dataclass
class Product:
"""Represents a product available for purchase."""
product_id: int
name: str
price: float
stock: int
category: str
description: str = ""
def update_stock(self, quantity: int) -> None:
"""Reduce stock when item is ordered."""
self.stock = max(0, self.stock - quantity)
def is_available(self, quantity: int = 1) -> bool:
"""Check if product can be ordered."""
return self.stock >= quantity
def get_price(self) -> float:
"""Return current product price."""
return self.price
@dataclass
class OrderItem:
"""Represents a single line item in an order."""
item_id: int
product: Product
quantity: int
unit_price: float
def get_subtotal(self) -> float:
"""Calculate line item total."""
return self.unit_price * self.quantity
def get_product_name(self) -> str:
"""Get the name of the ordered product."""
return self.product.name
class PaymentMethod(ABC):
"""Abstract base class for all payment methods."""
def __init__(self, amount: float):
self.amount = amount
self.status = "PENDING"
@abstractmethod
def process(self) -> bool:
"""Process the payment. Must be implemented by subclasses."""
pass
@abstractmethod
def refund(self) -> bool:
"""Refund the payment. Must be implemented by subclasses."""
pass
class CreditCardPayment(PaymentMethod):
"""Payment via credit or debit card."""
def __init__(self, amount: float, card_number: str, cvv: str):
super().__init__(amount)
self.card_number = card_number
self.cvv = cvv
def process(self) -> bool:
"""Process credit card payment."""
# Validate card
if not self._validate_card():
self.status = "FAILED"
return False
# Simulate payment processing
self.status = "COMPLETED"
return True
def refund(self) -> bool:
"""Refund the credit card payment."""
if self.status == "COMPLETED":
self.status = "REFUNDED"
return True
return False
def _validate_card(self) -> bool:
"""Validate card format."""
return len(self.card_number) == 16 and len(self.cvv) == 3
class PayPalPayment(PaymentMethod):
"""Payment via PayPal."""
def __init__(self, amount: float, email: str):
super().__init__(amount)
self.email = email
def process(self) -> bool:
"""Process PayPal payment."""
self.status = "COMPLETED"
return True
def refund(self) -> bool:
"""Refund PayPal payment."""
if self.status == "COMPLETED":
self.status = "REFUNDED"
return True
return False
@dataclass
class Order:
"""Represents a customer order."""
order_id: int
customer_id: int
date: datetime
status: OrderStatus = OrderStatus.PENDING
items: List[OrderItem] = field(default_factory=list)
shipping_address: Optional[ShippingAddress] = None
payment: Optional[PaymentMethod] = None
total: float = 0.0
discount_percent: float = 0.0
def add_item(self, item: OrderItem) -> None:
"""Add an item to the order."""
self.items.append(item)
def calculate(self) -> float:
"""Calculate order total before discount."""
self.total = sum(item.get_subtotal() for item in self.items)
return self.total
def apply_discount(self) -> float:
"""Apply discount and return final total."""
if self.total == 0:
self.calculate()
discount_amount = self.total * (self.discount_percent / 100)
return self.total - discount_amount
def finalize_payment(self, payment: PaymentMethod) -> bool:
"""Attach payment and process it."""
self.payment = payment
if payment.process():
self.status = OrderStatus.CONFIRMED
return True
return False
def get_summary(self) -> dict:
"""Get order summary."""
final_total = self.apply_discount()
return {
"order_id": self.order_id,
"item_count": len(self.items),
"subtotal": self.total,
"discount": self.discount_percent,
"final_total": final_total,
"status": self.status.value
}
@dataclass
class Customer:
"""Represents a customer in the e-commerce system."""
customer_id: int
name: str
email: str
membership_level: int = 0
is_active: bool = True
orders: List[Order] = field(default_factory=list)
shipping_address: Optional[ShippingAddress] = None
def get_orders(self) -> List[Order]:
"""Retrieve all orders for this customer."""
return self.orders
def place_order(self, order: Order) -> bool:
"""Place a new order if customer is active and address is valid."""
if not self.is_active:
return False
if self.shipping_address and not self.shipping_address.is_valid():
return False
order.customer_id = self.customer_id
order.shipping_address = self.shipping_address
self.orders.append(order)
return True
def calculate_discount(self) -> float:
"""Calculate loyalty discount based on membership level."""
membership_discounts = {
0: 0.0, # Standard customer: 0%
1: 0.05, # Silver: 5%
2: 0.10, # Gold: 10%
3: 0.15 # Platinum: 15%
}
return membership_discounts.get(self.membership_level, 0.0)
def upgrade_membership(self) -> None:
"""Upgrade customer to next membership level."""
if self.membership_level < 3:
self.membership_level += 1
def deactivate(self) -> None:
"""Deactivate customer account."""
self.is_active = False
# Example usage demonstrating class relationships
if __name__ == "__main__":
# Create shipping address
address = ShippingAddress(
street="456 Oak Avenue",
city="Seattle",
zip_code="98101",
country="USA"
)
# Create customer
customer = Customer(
customer_id=101,
name="Jane Smith",
email="jane.smith@example.com",
membership_level=2, # Gold member
shipping_address=address
)
# Create products
laptop = Product(1, "ThinkPad X1", 1299.99, 5, "Electronics", "Professional laptop")
monitor = Product(2, "4K Monitor", 599.99, 12, "Electronics", "Ultra HD display")
# Create order items
item1 = OrderItem(1, laptop, 1, 1299.99)
item2 = OrderItem(2, monitor, 1, 599.99)
# Create order
order = Order(
order_id=1001,
customer_id=customer.customer_id,
date=datetime.now(),
items=[item1, item2]
)
# Set discount for Gold member
order.discount_percent = customer.calculate_discount() * 100
# Place order
if customer.place_order(order):
print("Order placed successfully!")
# Calculate totals
subtotal = order.calculate()
final_total = order.apply_discount()
print(f"\n--- Order Summary ---")
print(f"Order ID: {order.order_id}")
print(f"Items: {len(order.items)}")
print(f"Subtotal: ${subtotal:.2f}")
print(f"Discount: {customer.calculate_discount()*100}%")
print(f"Final Total: ${final_total:.2f}")
# Process payment
payment = CreditCardPayment(final_total, "4111111111111111", "123")
if order.finalize_payment(payment):
print(f"Payment Status: {payment.status}")
print(f"Order Status: {order.status.value}")
Product, not Products).
Class diagrams are the blueprint for object-oriented software. They bridge the gap between requirements and code, enabling teams to communicate design decisions clearly. Mastering class diagram notation—understanding relationships, multiplicities, and visibility—is essential for software architects and developers.
Start with simple diagrams, focus on key relationships, and always align your diagrams with actual code. Used effectively, class diagrams become invaluable documentation that prevents misunderstandings and guides implementation.