UML类图
类图(class diagram) 描述系统中的对象类型,以及存在于它们之间的各种静态关系。
正向工程(forward engineering)
在编写代码之前画UML图。
逆向工程(reverse engineering)
从已有代码建造UML图,目的是帮助人们理解代码。
案例1:电商系统类图

from typing import Listclass User:def __init__(self, user_id: str, name: str):self.user_id = user_id self._name = name self.__password = "" def login(self, password: str) -> bool: """验证用户登录"""return password == self.__passwordclass Customer(User): def __init__(self, user_id: str, name: str):super().__init__(user_id, name)self.cart = ShoppingCart() def place_order(self) -> Order:"""创建订单"""return Order(self, self.cart.items)class Seller(User): def __init__(self, user_id: str, name: str, store: Store):super().__init__(user_id, name)self.store = store def add_product(self, product: Product):"""添加商品到店铺"""self.store.products.append(product)class Product:def __init__(self, product_id: str, name: str, price: float):self.product_id = product_idself.name = nameself.price = priceclass ShoppingCart:def __init__(self):self.items: List[Product] = [] def add_item(self, product: Product):self.items.append(product)def calculate_total(self) -> float:return sum(item.price for item in self.items)class Order:def __init__(self, customer: Customer, items: List[Product]):self.customer = customer self.items = itemsself.status = "Pending"def process_payment(self, payment: PaymentProcessor): payment.process(self.calculate_total())def calculate_total(self) -> float:return sum(item.price for item in self.items)class Store:def __init__(self, store_id: str, name: str):self.store_id = store_idself.name = nameself.products: List[Product] = []
class PaymentProcessor(ABC): @abstractmethoddef process(self, amount: float):passclass CreditCardProcessor(PaymentProcessor): def process(self, amount: float):print(f"Processing credit card payment: ${amount:.2f}")class PayPalProcessor(PaymentProcessor): def process(self, amount: float):print(f"Processing PayPal payment: ${amount:.2f}")
案例2:车辆租赁系统类图

from abc import ABC, abstractmethod
from enum import Enum
from datetime import dateclass VehicleType(Enum): CAR = 1TRUCK = 2SUV = 3MOTORCYCLE = 4class AbstractVehicle(ABC): def __init__(self, license_plate: str, model: str, year: int):self.license_plate = license_plateself.model = modelself.year = yearself.available = True@abstractmethoddef get_rental_rate(self) -> float:passclass Car(AbstractVehicle): def __init__(self, license_plate: str, model: str, year: int, seats: int):super().__init__(license_plate, model, year)self.seats = seatsdef get_rental_rate(self) -> float: return 50.0 + (self.seats * 5)class Truck(AbstractVehicle): def __init__(self, license_plate: str, model: str, year: int, capacity: float):super().__init__(license_plate, model, year)self.capacity = capacity def get_rental_rate(self) -> float:return 100.0 + (self.capacity * 20)class RentalAgency:def __init__(self, name: str):self.name = nameself.fleet: List[AbstractVehicle] = [] self.rentals: List[RentalContract] = [] def add_vehicle(self, vehicle: AbstractVehicle):self.fleet.append(vehicle)def rent_vehicle(self, customer: Customer, vehicle: AbstractVehicle, start_date: date, end_date: date):if vehicle.available:contract = RentalContract(customer, vehicle, start_date, end_date)self.rentals.append(contract)vehicle.available = Falsereturn contractreturn Noneclass Customer:def __init__(self, customer_id: str, name: str):self.customer_id = customer_idself.name = nameself.license_number = ""class RentalContract: def __init__(self, customer: Customer, vehicle: AbstractVehicle, start_date: date, end_date: date):self.customer = customerself.vehicle = vehicleself.start_date = start_dateself.end_date = end_dateself.total_cost = self.calculate_cost()def calculate_cost(self) -> float:days = (self.end_date - self.start_date).daysreturn days * self.vehicle.get_rental_rate()def generate_invoice(self, printer: InvoicePrinter): printer.print_invoice(self)class InvoicePrinter: def print_invoice(self, contract: RentalContract):print(f"Invoice for {contract.customer.name}")print(f"Vehicle: {contract.vehicle.model}")print(f"Total: ${contract.total_cost:.2f}")
案例3:学校管理系统类图

from abc import ABC, abstractmethod
from datetime import dateclass Person:def __init__(self, name: str, birth_date: date):self.name = nameself.birth_date = birth_datedef get_age(self) -> int:today = date.today()return today.year - self.birth_date.yearclass Researcher(ABC): @abstractmethoddef conduct_research(self, topic: str):passclass Teacher(Person): def __init__(self, name: str, birth_date: date, department: str):super().__init__(name, birth_date)self.department = departmentself.courses: List[Course] = [] def assign_course(self, course: 'Course'):self.courses.append(course)course.teacher = selfclass Professor(Teacher, Researcher): def __init__(self, name: str, birth_date: date, department: str, title: str):Teacher.__init__(self, name, birth_date, department)self.title = titledef conduct_research(self, topic: str): print(f"Conducting research on {topic}")def supervise_phd(self, student: 'PhdStudent'):student.advisor = selfclass Student(Person):def __init__(self, name: str, birth_date: date, student_id: str):super().__init__(name, birth_date)self.student_id = student_idself.enrolled_courses: List['Course'] = [] def enroll(self, course: 'Course'):self.enrolled_courses.append(course)course.students.append(self)class PhdStudent(Student, Researcher): def __init__(self, name: str, birth_date: date, student_id: str, research_topic: str):Student.__init__(self, name, birth_date, student_id)self.research_topic = research_topicself.advisor: Professor = None def conduct_research(self, topic: str): print(f"Conducting PhD research on {topic}")class Course:def __init__(self, course_code: str, name: str):self.course_code = course_codeself.name = nameself.teacher: Teacher = None self.students: List[Student] = [] def add_student(self, student: Student):self.students.append(student)student.enrolled_courses.append(self)class Department:def __init__(self, name: str):self.name = nameself.faculty: List[Teacher] = [] self.courses: List[Course] = [] def hire_teacher(self, teacher: Teacher):self.faculty.append(teacher)def add_course(self, course: Course):self.courses.append(course)class EnrollmentSystem: def enroll_student(self, student: Student, course: Course):if student not in course.students:student.enroll(course)return Truereturn False
综合案例:带抽象接口和静态方法的电商系统

from abc import ABC, abstractmethod
from datetime import datetime
class ILogger(ABC):@abstractmethoddef log(self, message: str):pass
class ConsoleLogger(ILogger):def log(self, message: str):print(f"[{datetime.now()}] {message}")class FileLogger(ILogger):def __init__(self, filename: str):self.filename = filenamedef log(self, message: str):with open(self.filename, "a") as file:file.write(f"[{datetime.now()}] {message}\n")
class ValidationUtils:@staticmethoddef is_valid_email(email: str) -> bool:return "@" in email and "." in email.split("@")[-1]@staticmethoddef is_valid_phone(phone: str) -> bool:return phone.isdigit() and len(phone) >= 7
class UserService:def __init__(self, logger: ILogger):self.logger = loggerdef register_user(self, name: str, email: str, phone: str):if not ValidationUtils.is_valid_email(email):self.logger.log(f"Invalid email: {email}")return Falseif not ValidationUtils.is_valid_phone(phone):self.logger.log(f"Invalid phone: {phone}")return Falseself.logger.log(f"User {name} registered with {email}")return True
class LoggerFactory:@staticmethoddef create_logger(logger_type: str) -> ILogger:if logger_type == "console":return ConsoleLogger()elif logger_type == "file":return FileLogger("app.log")else:raise ValueError("Invalid logger type")
UML类图要素总结
UML要素 | Python代码表现 | UML符号 | 说明 |
---|
类(Class) | class Person: | 矩形框 | 包含类名、属性和方法 |
抽象类 | class AbstractVehicle(ABC): | 斜体类名 | 包含抽象方法 |
接口 | class Researcher(ABC): | <<interface>> | 只包含抽象方法 |
属性 | self.name: str | +name: str | + 公有, - 私有, # 保护 |
方法 | def get_age(self): | +get_age(): int | 类行为定义 |
继承 | class Teacher(Person): | 空心三角+实线 | 泛化关系(is-a) |
实现 | class Professor(Researcher): | 空心三角+虚线 | 实现接口方法 |
组合 | self.cart = ShoppingCart() | 实心菱形+实线 | 强拥有关系(同生命周期) |
聚合 | self.fleet: List[Vehicle] = [] | 空心菱形+实线 | 弱拥有关系(可独立存在) |
关联 | self.teacher: Teacher = None | 实线箭头 | 对象间持久引用关系 |
依赖 | def process_payment(payment): | 虚线箭头 | 临时使用关系 |
枚举 | class VehicleType(Enum): | <<enumeration>> | 固定值集合 |
多重继承 | class Professor(Teacher, Researcher): | 多个空心三角 | 继承多个父类 |
UML元素 | UML表示法 | Python实现 |
---|
抽象接口 | <<interface>> + 斜体名称 | class Interface(ABC): + @abstractmethod |
接口实现 | 虚线空心三角箭头 (…|>) | 实现接口的所有抽象方法 |
静态方法 | {static} 标记或方法名下划线 | @staticmethod 装饰器 |
类方法 | {classmethod} 标记 | @classmethod 装饰器 |
抽象方法 | {abstract} 标记 + 斜体方法名 | @abstractmethod 装饰器 |
依赖关系 | 虚线箭头 (–>) | 方法参数或局部变量中使用 |
创建关系 | 虚线箭头 + <<create>> 构造型 | 工厂方法创建对象 |
一些细节
关于【箭头方向】
箭头方向在UML中表示导航性(Navigability):
箭头类型 | 表示 | 代码等价 |
---|
无箭头 | 双向导航(默认) | 双方相互持有引用 |
→ | 单向导航 | 只有源头类知道目标类 |
⬌ | 双向导航 | 双方相互持有引用 |
◁/▷ | 箭头端为被引用方 | 箭头指向的类是被持有的类 |
关于【多重性】
多重性定义对象之间的数量关系,常见表示法:
表示法 | 含义 | 示例说明 |
---|
1 | 恰好1个 | 每个人有1个心脏(组合关系) |
0..1 | 0或1个 | 学生可能有0或1个导师(关联关系) |
1..* | 1个或多个 | 订单必须包含至少1个商品(组合关系) |
0..* | 0或多个 | 部门可以有0或多个员工(聚合关系) |
n | 恰好n个 | 三角形有3条边(组合关系) |
m..n | m到n个 | 课程有3-50名学生(关联关系) |
* | 无限多个(同0..* ) | 社交媒体用户有多个好友(关联关系) |
汇总
要素类型 | UML表示法 | 代码表现 | 多重性 | 箭头方向 | 生命周期关系 |
---|
类(Class) | 矩形框(类名、属性、方法) | class MyClass: | 不适用 | 无 | 独立存在 |
抽象类 | 类名斜体 | class MyClass(ABC): | 不适用 | 无 | 独立存在 |
接口 | <<interface>> + 类框或圆圈 | class MyInterface(ABC): | 不适用 | 无 | 独立存在 |
枚举 | <<enumeration>> + 枚举值 | class MyEnum(Enum): | 不适用 | 无 | 独立存在 |
属性 | [可见性] 属性名: 类型 [= 默认值] | self.attr = value | 不适用 | 无 | 随对象存在 |
方法 | [可见性] 方法名(参数): 返回类型 | def method(self): | 不适用 | 无 | 随对象存在 |
抽象方法 | 斜体或{abstract} | @abstractmethod | 不适用 | 无 | 随抽象类存在 |
静态方法 | {static} 或下划线 | @staticmethod | 不适用 | 无 | 类加载时存在 |
类方法 | {classmethod} | @classmethod | 不适用 | 无 | 类加载时存在 |
继承(泛化) | 空心三角箭头 + 实线 | class Child(Parent): | 不适用 | 子类→父类 | 子类依赖父类 |
接口实现 | 空心三角箭头 + 虚线 | 实现接口所有方法 | 不适用 | 实现类→接口 | 实现类依赖接口 |
关联 | 实线(可带箭头) | 类属性为另一类对象 | 两端可设置 | 可选(表示导航方向) | 相互独立 |
聚合 | 空心菱形 + 实线 | 外部传入对象(self.parts = [ext_obj] ) | 整体端通常为1 | 菱形→整体 | 部分可独立于整体 |
组合 | 实心菱形 + 实线 | 内部创建对象(self.part = Part() ) | 整体端通常为1 | 菱形→整体 | 部分依赖整体 |
依赖 | 虚线箭头 | 局部变量/参数/静态调用 | 不适用 | 使用方→被依赖方 | 临时关系 |