深入理解系统:UML类图

发布于:2025-06-05 ⋅ 阅读:(24) ⋅ 点赞:(0)

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 菱形→整体 部分依赖整体
依赖 虚线箭头 局部变量/参数/静态调用 不适用 使用方→被依赖方 临时关系

网站公告

今日签到

点亮在社区的每一天
去签到