UML類圖
類圖(class diagram) 描述系統中的對象類型,以及存在于它們之間的各種靜態關系。
正向工程(forward engineering)
在編寫代碼之前畫UML圖。
逆向工程(reverse engineering)
從已有代碼建造UML圖,目的是幫助人們理解代碼。
案例1:電商系統類圖
# UML類圖元素:類、屬性、方法、繼承、關聯、聚合
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:車輛租賃系統類圖
# UML類圖元素:抽象類、枚舉、組合、聚合、依賴
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:學校管理系統類圖
# UML類圖元素:多重繼承、接口實現、依賴、關聯
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 False# 注冊邏輯...self.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 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 | 菱形→整體 | 部分依賴整體 |
依賴 | 虛線箭頭 | 局部變量/參數/靜態調用 | 不適用 | 使用方→被依賴方 | 臨時關系 |