← 返回 Day 37 正文

Day 37 · 图解

Day 037 — 组合与聚合:图解

Mermaid 与 ASCII 示意图,帮助理解组合、聚合、依赖注入和组合优于继承


1️⃣ 组合 vs 继承

继承的「脆弱的基类」问题

flowchart TB subgraph 继承 A[Base Class] --> B[Derived A] A --> C[Derived B] A --> D[Derived C] end subgraph 修改影响 CHANGE["修改 Base 的 fly() 方法"] end CHANGE -.->|影响所有子类!| B CHANGE -.->|可能不适用| C CHANGE -.->|可能导致 Bug| D
flowchart TB
    subgraph 继承
        A[Base Class] --> B[Derived A]
        A --> C[Derived B]
        A --> D[Derived C]
    end

    subgraph 修改影响
        CHANGE["修改 Base 的 fly() 方法"]
    end

    CHANGE -.->|影响所有子类!| B
    CHANGE -.->|可能不适用| C
    CHANGE -.->|可能导致 Bug| D

组合的优势

flowchart TB subgraph 组合 CAR[Car] --> E[Engine] CAR --> T[Tire 1..4] CAR --> GPS[GPS] end subgraph 更换组件 E2["V8 Engine"] -.->|替换| E T2["Winter Tire"] -.->|替换| T end note[CAR 不受影响<br/>组件可以独立替换]
flowchart TB
    subgraph 组合
        CAR[Car] --> E[Engine]
        CAR --> T[Tire 1..4]
        CAR --> GPS[GPS]
    end

    subgraph 更换组件
        E2["V8 Engine"] -.->|替换| E
        T2["Winter Tire"] -.->|替换| T
    end

    note[CAR 不受影响<br/>组件可以独立替换]

2️⃣ 组合(Composition)关系图

classDiagram class House { +rooms: list[Room] +address: str +__init__() } class Room { +name: str +area: float +__init__(name) } House *-- Room : 组合 note for House: House 创建 Room note for House: House 销毁 → Room 也销毁 note for Room: Room 不能独立于 House 存在
classDiagram
    class House {
        +rooms: list[Room]
        +address: str
        +__init__()
    }

    class Room {
        +name: str
        +area: float
        +__init__(name)
    }

    House *-- Room : 组合

    note for House: House 创建 Room
    note for House: House 销毁 → Room 也销毁
    note for Room: Room 不能独立于 House 存在

组合的生命周期

┌─── 时间线 ────────────────────────────────────┐

Car().__init__():
    ├── Engine.__init__()    ← 组合对象在内部创建
    ├── Transmission.__init__()
    └── FuelTank.__init__(55.0)

... 驾驶 ...

del car  (或 car 离开作用域):
    ├── FuelTank.__del__()   ← 组合对象自动销毁
    ├── Transmission.__del__()
    └── Engine.__del__()

✅ 生命周期完全由 Car 管理

3️⃣ 聚合(Aggregation)关系图

classDiagram class SchoolClass { +name: str +add_student(Student) +add_course(Course) +remove_student(Student) } class Student { +name: str +student_id: str } class Course { +name: str +code: str } SchoolClass o-- Student : 聚合 SchoolClass o-- Course : 聚合 note for SchoolClass: Student 从外部传入 note for SchoolClass: SchoolClass 销毁 → Student 继续存在
classDiagram
    class SchoolClass {
        +name: str
        +add_student(Student)
        +add_course(Course)
        +remove_student(Student)
    }

    class Student {
        +name: str
        +student_id: str
    }

    class Course {
        +name: str
        +code: str
    }

    SchoolClass o-- Student : 聚合
    SchoolClass o-- Course : 聚合

    note for SchoolClass: Student 从外部传入
    note for SchoolClass: SchoolClass 销毁 → Student 继续存在

聚合的生命周期

┌─── 时间线 ────────────────────────────────────┐

外部创建:
alice = Student("Alice", "S001")

class_a = SchoolClass("2026-1")
class_a.add_student(alice)    ← 聚合: 引用外部对象

del class_a                   ← 班级销毁
# alice 仍然存在!              ← 学生继续存在
print(alice.name)             → "Alice"

✅ 部分可以独立于整体存在

4️⃣ 组合 vs 聚合 对比

flowchart TB subgraph 组合 Composition C1[整体] -->|创建| C2[部分] C1 -->|拥有| C3[部分] C1 -.->|销毁| C4[部分也销毁] end subgraph 聚合 Aggregation A1[整体] -->|引用| A2[部分] A1 -->|使用| A3[部分] A1 -.->|销毁| A4[部分继续存在] end
flowchart TB
    subgraph 组合 Composition
        C1[整体] -->|创建| C2[部分]
        C1 -->|拥有| C3[部分]
        C1 -.->|销毁| C4[部分也销毁]
    end

    subgraph 聚合 Aggregation
        A1[整体] -->|引用| A2[部分]
        A1 -->|使用| A3[部分]
        A1 -.->|销毁| A4[部分继续存在]
    end

区别总结

特征                 组合                    聚合
══════════════       ══════════              ══════════

关系强度             强                      弱
生命周期             整体管理部分             部分独立
创建时机             整体构造时创建           从外部传入
销毁影响             部分一起销毁             部分继续存在
代码实现             构造器中 new             参数传入或 add()
例                   汽车 → 引擎             班级 → 学生
                    HTML → DOM 节点          图书馆 → 书籍
                    订单 → 订单项             公司 → 员工

5️⃣ 依赖注入(Dependency Injection)

flowchart LR subgraph 紧耦合 A["class Service:"] A1[" logger = FileLogger()"] A1 -->|硬编码| AL["只能使用 FileLogger"] end subgraph 松耦合(DI) B["class Service:"] B1[" def __init__(self, logger):"] B1 -->|注入| BL["可以使用任意 Logger"] end subgraph 注入方式 C1["构造器注入<br/>__init__(self, dep)"] C2["Setter 注入<br/>set_dep(self, dep)"] C3["方法注入<br/>method(self, dep)"] end
flowchart LR
    subgraph 紧耦合
        A["class Service:"]
        A1["    logger = FileLogger()"]
        A1 -->|硬编码| AL["只能使用 FileLogger"]
    end

    subgraph 松耦合(DI)
        B["class Service:"]
        B1["    def __init__(self, logger):"]
        B1 -->|注入| BL["可以使用任意 Logger"]
    end

    subgraph 注入方式
        C1["构造器注入<br/>__init__(self, dep)"]
        C2["Setter 注入<br/>set_dep(self, dep)"]
        C3["方法注入<br/>method(self, dep)"]
    end

依赖注入的生命周期管理

┌─── 依赖注入流程 ──────────────────────────────┐

外部创建依赖:
    logger = FileLogger()
    gps = GPS("高德")

┌─── 注入到服务 ───────────────────┐
│ car = Car(brand, model)          │
│ car.install_gps(gps)   ← 注入     │
└──────────────────────────────────┘

外部管理依赖生命周期:
    # GPS 可以切换到另一个 Car
    car2 = Car("Honda", "Accord")
    car2.install_gps(gps)  ← 复用 GPS

    # 也可以替换
    gps2 = GPS("百度导航")
    car.install_gps(gps2)  ← 替换

✅ 依赖的创建和销毁由外部管理

6️⃣ 「组合优于继承」对比图

flowchart TB subgraph 继承方案 ❌ I1[Bird] --> I2[fly] I1 --> I3[eat] I1 --> I4[sleep] I2 -.->|问题| I5[Penguin 不会飞] end subgraph 组合方案 ✅ C1["Bird<br/>- fly_behavior<br/>- eat_behavior<br/>- sleep_behavior"] C1 -->|调用| C2[FlyBehavior.fly] C1 -->|调用| C3[EatBehavior.eat] C1 -->|调用| C4[SleepBehavior.sleep] C5["Penguin<br/>- walk_behavior<br/>- eat_behavior<br/>- sleep_behavior"] C5 -->|调用| C6[WalkBehavior.walk] end
flowchart TB
    subgraph 继承方案 ❌
        I1[Bird] --> I2[fly]
        I1 --> I3[eat]
        I1 --> I4[sleep]
        I2 -.->|问题| I5[Penguin 不会飞]
    end

    subgraph 组合方案 ✅
        C1["Bird<br/>- fly_behavior<br/>- eat_behavior<br/>- sleep_behavior"]
        C1 -->|调用| C2[FlyBehavior.fly]
        C1 -->|调用| C3[EatBehavior.eat]
        C1 -->|调用| C4[SleepBehavior.sleep]

        C5["Penguin<br/>- walk_behavior<br/>- eat_behavior<br/>- sleep_behavior"]
        C5 -->|调用| C6[WalkBehavior.walk]
    end

代码复用对比

方案 A:继承                         方案 B:组合(推荐)
════════════════                    ════════════════════

class Bird:                         class FlyBehavior:
    def fly(self): ...                  def fly(self): ...
    def eat(self): ...

                                    class EatBehavior:
class Penguin(Bird):                     def eat(self): ...
    def fly(self):  # ❌ 会飞
        raise ...                    class Bird:
                                       def __init__(self):
class Ostrich(Bird):                      self.fly = FlyBehavior()
    def fly(self):  # ❌ 会飞               self.eat = EatBehavior()
        raise ...

                                    class Penguin:
# 层次越深问题越多                      def __init__(self):
# 违反 LSP 原则                           self.eat = EatBehavior()
                                        self.walk = WalkBehavior()
                                    # ✅ 只组合需要的功能

7️⃣ Car 模型架构

classDiagram class Car { -_engine: Engine -_transmission: Transmission -_fuel_tank: FuelTank -_tires: list[Tire] -_gps: GPS +start() str +drive(km, speed) list +stop() str +replace_tire(pos, tire) str +install_gps(gps) str } class Engine { -_running: bool -_rpm: int +start() str +accelerate(rpm) tuple +stop() str } class Transmission { -_gear: str +shift(gear) str +auto_shift(rpm) str } class FuelTank { -_level: float +refill(liters) str +consume(liters) bool } class Tire { -_pressure: float -_tread_depth: float +inflate(psi) str +check_tread() str +wear(km) void } class GPS { +set_destination(dest) str +navigate() str } Car *-- Engine : 组合 Car *-- Transmission : 组合 Car *-- FuelTank : 组合 Car o-- Tire : 聚合 Car o-- GPS : 依赖注入
classDiagram
    class Car {
        -_engine: Engine
        -_transmission: Transmission
        -_fuel_tank: FuelTank
        -_tires: list[Tire]
        -_gps: GPS
        +start() str
        +drive(km, speed) list
        +stop() str
        +replace_tire(pos, tire) str
        +install_gps(gps) str
    }

    class Engine {
        -_running: bool
        -_rpm: int
        +start() str
        +accelerate(rpm) tuple
        +stop() str
    }

    class Transmission {
        -_gear: str
        +shift(gear) str
        +auto_shift(rpm) str
    }

    class FuelTank {
        -_level: float
        +refill(liters) str
        +consume(liters) bool
    }

    class Tire {
        -_pressure: float
        -_tread_depth: float
        +inflate(psi) str
        +check_tread() str
        +wear(km) void
    }

    class GPS {
        +set_destination(dest) str
        +navigate() str
    }

    Car *-- Engine : 组合
    Car *-- Transmission : 组合
    Car *-- FuelTank : 组合
    Car o-- Tire : 聚合
    Car o-- GPS : 依赖注入

关系类型标识

┌─────────────────────────┐
│      关系类型图例         │
│                         │
│  *--  组合 Composition   │
│  o--  聚合 Aggregation   │
│  <|-- 继承 Inheritance   │
│  -->  依赖 Dependency    │
│  ..>  使用 Usage         │
└─────────────────────────┘