UML类图
类图(class diagram) 描述系统中的对象类型 ,以及存在于它们之间的各种静态关系。
正向工程(forward engineering)
在编写代码之前画UML图。
逆向工程(reverse engineering)
从已有代码建造UML图,目的是帮助人们理解代码。
案例1:电商系统类图
python
复制代码
# UML类图元素:类、属性、方法、继承、关联、聚合
from typing import List
class 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.__password
class 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_id
self.name = name
self.price = price
class 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 = items
self.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_id
self.name = name
self.products: List[Product] = [] # 聚合关系
# 接口实现 (依赖倒置)
class PaymentProcessor(ABC): # 抽象类/接口
@abstractmethod
def process(self, amount: float):
pass
class 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:车辆租赁系统类图
python
复制代码
# UML类图元素:抽象类、枚举、组合、聚合、依赖
from abc import ABC, abstractmethod
from enum import Enum
from datetime import date
class VehicleType(Enum): # 枚举类
CAR = 1
TRUCK = 2
SUV = 3
MOTORCYCLE = 4
class AbstractVehicle(ABC): # 抽象类
def __init__(self, license_plate: str, model: str, year: int):
self.license_plate = license_plate
self.model = model
self.year = year
self.available = True
@abstractmethod
def get_rental_rate(self) -> float:
pass
class Car(AbstractVehicle): # 继承
def __init__(self, license_plate: str, model: str, year: int, seats: int):
super().__init__(license_plate, model, year)
self.seats = seats
def 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 = name
self.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 = False
return contract
return None
class Customer:
def __init__(self, customer_id: str, name: str):
self.customer_id = customer_id
self.name = name
self.license_number = ""
class RentalContract: # 组合类
def __init__(self, customer: Customer, vehicle: AbstractVehicle,
start_date: date, end_date: date):
self.customer = customer
self.vehicle = vehicle
self.start_date = start_date
self.end_date = end_date
self.total_cost = self.calculate_cost()
def calculate_cost(self) -> float:
days = (self.end_date - self.start_date).days
return 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:学校管理系统类图
python
复制代码
# UML类图元素:多重继承、接口实现、依赖、关联
from abc import ABC, abstractmethod
from datetime import date
class Person:
def __init__(self, name: str, birth_date: date):
self.name = name
self.birth_date = birth_date
def get_age(self) -> int:
today = date.today()
return today.year - self.birth_date.year
class Researcher(ABC): # 接口
@abstractmethod
def conduct_research(self, topic: str):
pass
class Teacher(Person): # 单继承
def __init__(self, name: str, birth_date: date, department: str):
super().__init__(name, birth_date)
self.department = department
self.courses: List[Course] = [] # 双向关联
def assign_course(self, course: 'Course'):
self.courses.append(course)
course.teacher = self
class Professor(Teacher, Researcher): # 多重继承
def __init__(self, name: str, birth_date: date, department: str, title: str):
Teacher.__init__(self, name, birth_date, department)
self.title = title
def conduct_research(self, topic: str): # 实现接口
print(f"Conducting research on {topic}")
def supervise_phd(self, student: 'PhdStudent'):
student.advisor = self
class Student(Person):
def __init__(self, name: str, birth_date: date, student_id: str):
super().__init__(name, birth_date)
self.student_id = student_id
self.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_topic
self.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_code
self.name = name
self.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 = name
self.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 True
return False
综合案例:带抽象接口和静态方法的电商系统
python
复制代码
from abc import ABC, abstractmethod
from datetime import datetime
# 抽象接口:日志服务
class ILogger(ABC):
@abstractmethod
def 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 = filename
def log(self, message: str):
with open(self.filename, "a") as file:
file.write(f"[{datetime.now()}] {message}\n")
# 带静态方法的工具类
class ValidationUtils:
@staticmethod
def is_valid_email(email: str) -> bool:
return "@" in email and "." in email.split("@")[-1]
@staticmethod
def is_valid_phone(phone: str) -> bool:
return phone.isdigit() and len(phone) >= 7
# 使用接口和静态方法的类
class UserService:
def __init__(self, logger: ILogger):
self.logger = logger
def register_user(self, name: str, email: str, phone: str):
# 使用静态方法验证
if not ValidationUtils.is_valid_email(email):
self.logger.log(f"Invalid email: {email}")
return False
if not ValidationUtils.is_valid_phone(phone):
self.logger.log(f"Invalid phone: {phone}")
return False
# 注册逻辑...
self.logger.log(f"User {name} registered with {email}")
return True
# 工厂类(使用静态方法创建对象)
class LoggerFactory:
@staticmethod
def 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 Interface(ABC): + @abstractmethod |
<<interface>> + 斜体名称 |
只包含抽象方法 |
属性 |
self.name: str |
+name: str |
+ 公有, - 私有, # 保护 |
方法 |
def get_age(self): 静态方法: @staticmethod 装饰器 类方法:@classmethod 装饰器 抽象方法: @abstractmethod 装饰器 |
+get_age(): int 静态方法:{static} 标记或方法名下划线 类方法:{classmethod} 标记 抽象方法:{abstract} 标记 + 斜体方法名 |
类行为定义 |
继承 |
class Teacher(Person): |
空心三角+实线 |
泛化关系(is-a) |
实现 |
class Professor(Researcher): |
空心三角+虚线 |
实现接口方法 |
组合 |
一对一:self.cart = ShoppingCart() 一对多:self.car=[Wheel(),Wheel(),Wheel(),Wheel(),Engine()] |
实心菱形+实线 |
强拥有关系(同生命周期) |
聚合 |
self.fleet: List[Vehicle] = [] |
空心菱形+实线 |
弱拥有关系(可独立存在) |
关联 |
self.teacher: Teacher = None |
实线箭头 |
对象间持久引用关系 |
依赖 |
def process_payment(payment): |
虚线箭头 |
临时使用(方法参数或局部变量中) |
枚举 |
class VehicleType(Enum): |
<<enumeration>> |
固定值集合 |
多重继承 |
class Professor(Teacher, Researcher): |
多个空心三角 |
继承多个父类 |
一些细节
关于【箭头方向】
箭头方向在UML中表示导航性(Navigability):
箭头类型 |
表示 |
代码等价 |
无箭头 |
双向导航(默认) |
双方相互持有引用 |
→ |
单向导航 |
只有源头类知道目标类 |
⬌ |
双向导航 |
双方相互持有引用 |
◁/▷ |
箭头端为被引用方 |
箭头指向的类是被持有的类 |
关于类关系的虚实
以下是对类图中类关系图形的完整总结,重点说明实心/空心、实线/虚线的区别:
1. 实线 vs 虚线
线条类型 |
关系强度 |
生命周期 |
代码对应 |
典型关系 |
实线 |
强关系 |
可能绑定 |
成员变量(属性) |
关联、聚合、组合 |
虚线 |
弱关系 |
无绑定 |
方法参数/局部变量 |
依赖、接口实现 |
2. 实心 vs 空心
填充类型 |
所有权 |
关系强度 |
典型符号位置 |
代表关系 |
实心 |
强所有权 |
最强 |
菱形端 |
组合关系 |
空心 |
弱所有权 |
中等 |
菱形端/三角端 |
聚合/继承/实现 |
完整关系对比图
关于【多重性】
多重性定义对象之间的数量关系,常见表示法:
表示法 |
含义 |
示例说明 |
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 |
菱形→整体 |
部分依赖整体 |
依赖 |
虚线箭头 |
局部变量/参数/静态调用 |
不适用 |
使用方→被依赖方 |
临时关系 |