UML类图
类图(class diagram) 描述系统中的对象类型,以及存在于它们之间的各种静态关系。
正向工程(forward engineering)
在编写代码之前画UML图。
逆向工程(reverse engineering)
从已有代码建造UML图,目的是帮助人们理解代码。
案例1:电商系统类图
# 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:车辆租赁系统类图
# 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:学校管理系统类图
# 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
综合案例:带抽象接口和静态方法的电商系统
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 | 菱形→整体 | 部分依赖整体 |
依赖 | 虚线箭头 | 局部变量/参数/静态调用 | 不适用 | 使用方→被依赖方 | 临时关系 |