目录

UML各种图

2026-01-02

任课老师:Zhao


一、UML

(一)UML类之间的关系

1. 依赖(Dependency)

定义回顾

依赖是临时性、弱关联的关系,依赖方不持有被依赖方实例,仅在方法中临时使用。

C++ 示例代码

#include <iostream>
#include <string>

// 被依赖方:日志类
class Logger {
public:
    void log(const std::string& msg) const {
        std::cout << "[Log] " << msg << std::endl;
    }
};

// 依赖方:业务服务类
class Service {
public:
    // 仅在方法中临时使用Logger,不持有其实例
    void doBusiness() {
        Logger logger; // 临时创建Logger实例
        logger.log("业务逻辑执行中"); // 依赖Logger的log功能
    }
};

int main() {
    Service service;
    service.doBusiness();
    return 0;
}

UML 图示(Mermaid)

classDiagram
    Service ..> Logger : 依赖 (虚线箭头)
    class Service {
        +doBusiness()
    }
    class Logger {
        +log(msg: string)
    }

分析

  • ServicedoBusiness 方法中临时创建 Logger 实例并调用其 log 方法,无成员变量持有 Logger,符合“临时性、弱关联”特点。
  • Loggerlog 方法签名修改(如参数类型变化),ServicedoBusiness 方法需同步修改,体现“被依赖方变化影响依赖方”。

2. 泛化(Generalization)

定义回顾

对应继承关系,子类继承父类属性和方法,遵循“is-a”逻辑,UML 用带空心三角的实线表示(子类指向父类)。

C++ 示例代码

#include <iostream>
#include <string>

// 父类(一般类):动物
class Animal {
protected:
    std::string name;
public:
    Animal(const std::string& n) : name(n) {}
    // 共性方法
    void eat() const {
        std::cout << name << " 进食" << std::endl;
    }
    void sleep() const {
        std::cout << name << " 睡觉" << std::endl;
    }
    // 虚方法,允许子类重写
    virtual void makeSound() const = 0; // 纯虚函数,抽象方法
};

// 子类(特殊类):狗
class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {}
    // 重写父类虚方法(个性扩展)
    void makeSound() const override {
        std::cout << name << " 汪汪叫" << std::endl;
    }
};

// 子类(特殊类):猫
class Cat : public Animal {
public:
    Cat(const std::string& n) : Animal(n) {}
    // 重写父类虚方法(个性扩展)
    void makeSound() const override {
        std::cout << name << " 喵喵叫" << std::endl;
    }
};

int main() {
    Dog dog("旺财");
    dog.eat();    // 复用父类方法
    dog.makeSound(); // 子类个性化实现

    Cat cat("咪宝");
    cat.sleep();  // 复用父类方法
    cat.makeSound(); // 子类个性化实现
    return 0;
}

UML 图示(Mermaid)

classDiagram
    Animal <|-- Dog : 继承 (空心三角实线)
    Animal <|-- Cat : 继承 (空心三角实线)
    class Animal {
        -name: string
        +Animal(name: string)
        +eat()
        +sleep()
        +makeSound()*
    }
    class Dog {
        +Dog(name: string)
        +makeSound()
    }
    class Cat {
        +Cat(name: string)
        +makeSound()
    }

分析

  • Dog/Cat 继承 Animal,遵循“Dog is an Animal”“Cat is an Animal”的“is-a”逻辑。
  • 父类 Animal 抽取共性(eat/sleep),子类通过重写 makeSound 实现个性扩展。
  • 纯虚函数 makeSound 强制子类实现,体现继承的多态特性。

3. 实现(Realization)

定义回顾

类实现接口/抽象类,需重写所有抽象方法,遵循“implements”逻辑,UML 用带空心三角的虚线表示(实现类指向接口)。

C++ 示例代码

#include <iostream>
#include <string>

// 接口(抽象类):用户服务
class UserService {
public:
    // 纯虚函数(接口契约)
    virtual std::string getUser(int id) const = 0;
    virtual void addUser(const std::string& name) const = 0;
    virtual ~UserService() = default; // 虚析构,避免内存泄漏
};

// 实现类:用户服务实现
class UserServiceImpl : public UserService {
public:
    // 重写接口所有抽象方法
    std::string getUser(int id) const override {
        return "用户ID: " + std::to_string(id) + ",名称:默认用户";
    }

    void addUser(const std::string& name) const override {
        std::cout << "添加用户:" << name << " 成功" << std::endl;
    }
};

int main() {
    UserService* service = new UserServiceImpl();
    std::cout << service->getUser(1001) << std::endl;
    service->addUser("张三");
    delete service;
    return 0;
}

UML 图示(Mermaid)

classDiagram
    UserService <|.. UserServiceImpl : 实现 (空心三角虚线)
    class UserService {
        +getUser(id: int): string*
        +addUser(name: string)*
    }
    class UserServiceImpl {
        +getUser(id: int): string
        +addUser(name: string)
    }

分析

  • UserService 作为接口(纯抽象类)定义“契约”(getUser/addUser),UserServiceImpl 实现该接口并完成所有抽象方法的具体逻辑。
  • C++ 无原生 interface 关键字,通过“纯虚函数 + 虚析构”模拟接口,符合“接口定义规范,实现类提供具体实现”的核心特点。
  • 支持多实现:若有 PayService 接口,UserServiceImpl 可同时继承 UserServicePayService(多继承)。

4. 关联(Association)

定义回顾

稳定、长期的结构关系,类之间持有对方实例引用,分单向/双向关联,遵循“has-a”逻辑,UML 用实线(带箭头表示单向)。

C++ 示例代码

#include <iostream>
#include <string>
#include <vector>

// 学生类
class Student;

// 教师类(单向关联:Teacher 持有 Student 引用)
class Teacher {
private:
    std::string name;
    std::vector<Student*> students; // 关联多个学生(1:N)
public:
    Teacher(const std::string& n) : name(n) {}
    void addStudent(Student* s);
    void showStudents() const;
    std::string getName() const { return name; }
};

// 学生类(双向关联:Student 持有 Teacher 引用)
class Student {
private:
    std::string name;
    std::vector<Teacher*> teachers; // 关联多个教师(N:M)
public:
    Student(const std::string& n) : name(n) {}
    void addTeacher(Teacher* t) {
        teachers.push_back(t);
    }
    std::string getName() const { return name; }
};

// 实现Teacher的addStudent方法
void Teacher::addStudent(Student* s) {
    students.push_back(s);
    s->addTeacher(this); // 双向关联:学生也关联教师
}

void Teacher::showStudents() const {
    std::cout << name << " 教授的学生:";
    for (const auto& s : students) {
        std::cout << s->getName() << " ";
    }
    std::cout << std::endl;
}

int main() {
    Teacher t1("王老师");
    Teacher t2("李老师");
    Student s1("张三");
    Student s2("李四");

    t1.addStudent(&s1);
    t1.addStudent(&s2);
    t2.addStudent(&s1);

    t1.showStudents(); // 王老师:张三 李四
    t2.showStudents(); // 李老师:张三
    return 0;
}

UML 图示(Mermaid)

classDiagram
    Teacher "1" -- "*" Student : "Accociation Both Side"
    
    class Teacher {
        -name: string
        -students: vector<Student*>
        +Teacher(name: string)
        +addStudent(s: Student*)
        +showStudents()
        +getName(): string
    }
    
    class Student {
        -name: string
        -teachers: vector<Teacher*>
        +Student(name: string)
        +addTeacher(t: Teacher*)
        +getName(): string
    }

分析

  • TeacherStudent双向关联,双方都持有对方的实例引用(vector 存储指针),符合“稳定、长期”的关联特点。
  • 关联线上标注的“1”“*”表示多重度:1个教师对应多个学生(1:N),1个学生对应多个教师(N:M)。
  • 遵循“has-a”逻辑:Teacher has students,Student has teachers。

5. 聚合(Aggregation)

定义回顾

关联的特殊形式,“整体-部分”关系,部分可脱离整体独立存在(生命周期不绑定),UML 用带空心菱形的实线表示(菱形指向整体)。

C++ 示例代码

#include <iostream>
#include <string>
#include <vector>

// 部分类:车轮
class Wheel {
private:
    std::string brand; // 车轮品牌
public:
    Wheel(const std::string& b) : brand(b) {}
    std::string getBrand() const { return brand; }
    // 车轮可独立存在,有自己的生命周期
    void repair() const {
        std::cout << "维修 " << brand << " 品牌车轮" << std::endl;
    }
};

// 整体类:汽车
class Car {
private:
    std::string model; // 车型
    std::vector<Wheel*> wheels; // 聚合多个车轮(整体持有部分引用)
public:
    Car(const std::string& m) : model(m) {}
    // 添加车轮(部分可从外部传入,不依赖Car创建)
    void addWheel(Wheel* w) {
        wheels.push_back(w);
    }
    void showWheels() const {
        std::cout << model << " 的车轮品牌:";
        for (const auto& w : wheels) {
            std::cout << w->getBrand() << " ";
        }
        std::cout << std::endl;
    }
    ~Car() {
        // 聚合关系:Car销毁时不销毁Wheel(部分可独立存在)
        std::cout << model << " 报废,但车轮可回收复用" << std::endl;
    }
};

int main() {
    // 1. 先创建车轮(部分独立存在)
    Wheel w1("米其林");
    Wheel w2("普利司通");
    Wheel w3("固特异");
    Wheel w4("马牌");

    // 2. 将车轮组装到汽车(整体)
    Car car("特斯拉Model 3");
    car.addWheel(&w1);
    car.addWheel(&w2);
    car.addWheel(&w3);
    car.addWheel(&w4);
    car.showWheels();

    // 3. 汽车销毁后,车轮仍可独立使用
    w1.repair();
    return 0;
}

UML 图示(Mermaid)

classDiagram
    Car o-- Wheel : 聚合(空心菱形,菱形指向Car)
    class Car {
        -model: string
        -wheels: vector<Wheel*>
        +Car(model: string)
        +addWheel(w: Wheel*)
        +showWheels()
        ~Car()
    }
    class Wheel {
        -brand: string
        +Wheel(brand: string)
        +getBrand(): string
        +repair()
    }

分析

  • Car(整体)聚合 Wheel(部分),符合“整体-部分”关系;Wheel 可先于 Car 创建,且 Car 销毁时不销毁 Wheel(部分可独立存在、复用)。
  • 空心菱形指向 Car(整体),体现“弱整体-部分”关系:车轮可从汽车拆卸,安装到其他汽车或单独维修。

6. 组合(Composition)

定义回顾

关联的特殊形式,“整体-部分”关系,部分生命周期与整体绑定(整体销毁时部分也销毁),UML 用带实心菱形的实线表示(菱形指向整体)。

C++ 示例代码

#include <iostream>
#include <string>

// 部分类:心脏
class Heart {
private:
    int beatRate; // 心率
public:
    Heart() : beatRate(75) {
        std::cout << "心脏创建,初始心率:" << beatRate << "次/分钟" << std::endl;
    }
    void beat() const {
        std::cout << "心脏跳动,心率:" << beatRate << "次/分钟" << std::endl;
    }
    ~Heart() {
        std::cout << "心脏停止跳动" << std::endl;
    }
};

// 整体类:人
class Person {
private:
    std::string name;
    Heart heart; // 组合关系:直接包含Heart对象(而非指针/引用)
public:
    Person(const std::string& n) : name(n), heart() { // 整体创建时,部分自动创建
        std::cout << name << " 出生" << std::endl;
    }
    void live() const {
        std::cout << name << " 存活中:";
        heart.beat();
    }
    ~Person() { // 整体销毁时,部分自动销毁
        std::cout << name << " 离世" << std::endl;
    }
};

int main() {
    {
        Person p("张三");
        p.live(); // 张三存活中:心脏跳动...
    } // 作用域结束,Person销毁,Heart也随之销毁

    // 无法单独创建Heart并复用(逻辑上,心脏不能脱离人独立存在)
    return 0;
}

UML 图示(Mermaid)

classDiagram
    Person *-- Heart : 组合(实心菱形,菱形指向Person)
    class Person {
        -name: string
        -heart: Heart
        +Person(name: string)
        +live()
        ~Person()
    }
    class Heart {
        -beatRate: int
        +Heart()
        +beat()
        ~Heart()
    }

分析

  • Person(整体)组合 Heart(部分),HeartPerson 的成员变量(而非指针/引用),体现“强整体-部分”关系。
  • 生命周期绑定:Person 创建时 Heart 自动构造,Person 销毁时 Heart 自动析构,符合“部分无法脱离整体独立存在”的核心特点。
  • 逻辑上,心脏不能脱离人单独存在,完全依赖整体,是组合关系的典型体现。

总结:6种关系的核心区别

| 关系 | 核心逻辑 | 生命周期 | UML 特征 | C++ 实现要点 | |————|—————-|—————-|—————————|———————————-| | 依赖 | 使用(use-a) | 临时 | 虚线箭头(指向被依赖方) | 方法内临时创建/调用,无成员引用 | | 泛化 | 继承(is-a) | 子类依赖父类 | 空心三角实线(子类→父类) | public 继承,重写父类方法 | | 实现 | 实现(implements) | 实现类依赖接口 | 空心三角虚线(实现类→接口) | 继承纯抽象类,重写所有纯虚函数 | | 关联 | 持有(has-a) | 长期绑定 | 实线(单向/双向) | 成员变量持有对方指针/引用 | | 聚合 | 整体-部分(弱)| 部分独立 | 空心菱形实线(菱形→整体) | 整体持有部分指针,不管理生命周期 | | 组合 | 整体-部分(强)| 部分依赖整体 | 实心菱形实线(菱形→整体) | 整体直接包含部分对象,管理生命周期 |

// 依赖:通过参数或局部变量
void process(Logger& logger) { /* ... */ }

// 泛化:公有继承
class Dog : public Animal { /* ... */ };

// 实现:纯虚函数
class IService { virtual void doWork() = 0; };

// 关联:成员指针
class Teacher { Student* student; };

// 聚合:不控制生命周期的指针
class Team { Member* member; };

// 组合:控制生命周期的对象
class Car { Engine engine; };  // Engine在Car内部创建

8. UML关联关系中“多重度”概念说明

“多重度”(Multiplicity)用于定义关联关系中,两个类的实例之间可以建立的对应数量关系,通常标注在关联线的两端,清晰界定实例交互的数量边界。

(1)常用多重度符号及含义

符号 名称 具体含义
1 一个 关联的一端必须且只能有1个实例
0..1 零或一个 关联的一端可以有0个实例(可选),也可以有1个实例
*0..* 零或多个 关联的一端可以有0个、1个或多个实例(数量无上限)
1..* 一个或多个 关联的一端至少有1个实例,也可以有多个实例
n..m(如2..5) 范围数量 关联的一端实例数量在n到m之间(包含n和m),n、m为具体数字

(2)典型应用场景示例

  • 1:1(一对一):一个实例仅对应另一个实例。例如“Person”与“IDCard”,一个人只能有一张身份证,一张身份证也仅属于一个人,关联线两端均标注1
  • 1:N(一对多):一个实例可对应多个实例,反之仅对应一个。例如“Department”与“Employee”,一个部门可有多个员工,一个员工仅属于一个部门,部门端标注1,员工端标注*
  • N:M(多对多):两端实例均可对应多个实例。例如“Student”与“Course”,一个学生可选多门课程,一门课程可被多个学生选择,关联线两端均标注*

(二)状态图

状态图(State Diagram)是UML行为图的一种,用于描述对象从创建到销毁的生命周期中,不同状态的转换逻辑,核心是“状态”与“转换”两大要素。以下是分步骤的绘制方法,适用于Markdown(需借助Mermaid语法渲染)或可视化工具(如Visio、StarUML)。

1. 明确绘制目标与核心要素

在动笔前先界定范围,避免信息冗余。状态图的核心要素只有3个,需提前梳理清楚:

  • 状态(State):对象的行为模式或属性集合,分3类
    • 初始状态:对象生命周期的起点,用“实心小圆”表示,一个图仅1个。
    • 普通状态:对象的常规行为,用“圆角矩形”表示(如“待支付”“已发货”)。
    • 终止状态:对象生命周期的终点,用“内部带实心圆的大圆”表示,可多个。
  • 转换(Transition):状态间的切换,用“带箭头的实线”表示,箭头上需标注“触发事件[守卫条件]/动作”(如“支付成功[金额≥0]/生成订单”)。
  • 对象(可选):若需明确归属,可在图顶部标注“对象名: 类名”(如“订单123: Order”)。

2. 按“生命周期流”梳理状态逻辑

以“电商订单”为例,按时间顺序拆解状态,避免逻辑断层,步骤如下:

  1. 确定起点:初始状态(订单创建)。
  2. 罗列普通状态:按流程拆解核心节点,如“待支付”→“支付中”→“已支付”→“已发货”→“已签收”。
  3. 补充分支状态:考虑异常或分支场景,如“待支付”可因“超时未支付”转入“已取消”(终止状态)。
  4. 确定终点:每个流程分支对应一个终止状态,如“已签收”“已取消”均为终止状态。

3. 用Mermaid语法编写Markdown状态图(实操示例)

Mermaid是Markdown中最常用的状态图渲染语法,无需手动拖拽,代码即图,以下是“电商订单”的完整示例:

stateDiagram-v2  
    [*] --> 待支付  : 初始状态→第一个普通状态,[*]代表初始
    待支付 --> 支付中 : 点击支付按钮/发起支付请求
    支付中 --> 已支付 : 支付成功[金额≥订单金额]/生成支付凭证
    支付中 --> 待支付 : 支付失败[网络异常]/提示重试
    待支付 --> 已取消 : 超时未支付[超过30分钟]/释放库存
    已支付 --> 已发货 : 商家点击发货/生成物流单号
    已发货 --> 已签收 : 买家确认收货/更新订单状态
    已签收 --> [*] : 订单完成/关闭生命周期(终止状态)
    已取消 --> [*] : 订单终止/释放资源(终止状态)
  • 语法说明
    • stateDiagram-v2:固定开头,确保渲染效果。
    • [*]:代表初始/终止状态(起点用[*]-->状态,终点用状态-->[*])。
    • 转换规则:源状态 --> 目标状态 : 触发事件[守卫条件]/执行动作,“[守卫条件]”和“/动作”可省略(如仅“点击支付”)。

4. 绘制注意事项(避坑指南)

  • 避免“状态冗余”:只保留影响行为的核心状态,如“订单”无需拆分“待支付-等待1分钟”“待支付-等待2分钟”。
  • 转换标注清晰:触发事件需具体(如“点击支付”而非“操作”),守卫条件需可判断(如“超时30分钟”而非“超时”)。
  • 逻辑闭环:每个普通状态都要有后续转换(除终止状态),避免“孤立状态”(如“已支付”必须能到“已发货”或异常状态)。

(三)数据流图(DFD)详解与完整示例

1. 定义与特点

  • 描述系统逻辑模型,仅描绘信息流动和处理情况,不涉及具体物理实现。
  • 直观易懂,便于技术人员与需求方(客户、用户)沟通,体现工程思想。
  • 属于行为模型、功能模型,不同于程序流程图。

2. 核心符号(术语)

符号类型 定义与说明 表示形式
外部实体 数据的源点或终点(如人、设备、其他系统)。 正方形
处理 数据转换器,将输入转换为输出。 圆形/矩形(带编号)
数据流 数据在系统内的有方向流动。 带箭头的线段
数据存储 数据的静止存储状态(如文件、数据库)。 两条平行线

3. 绘图规则总结

  • 加工:必须有输入和输出,标签用动词短语。
  • 数据存储:数据不能直接在存储间流动,必须通过加工;标签用名词短语。
  • 外部实体:数据不能直接从源点流向终点;标签用名词短语。
  • 数据流:单向流动,分叉/汇合有意义,不能回流,标签用名词短语。

4. 绘制方法与分层原则

  • 宏观原则:“由外向里”——先定系统边界,再画内部。
  • 分层结构
    • 顶层(0层):系统整体边界,只有一个加工。
    • 一级细化图:分解顶层加工为核心子加工(3-7个)。
    • 二级及以下细化图:对单一加工继续分解,直至每个加工功能单一。
  • 关键原则
    1. 保持平衡:子图的输入输出必须与父图对应加工的输入输出完全一致。
    2. 编号规范:加工编号体现层级(如1.0, 1.1, 1.1.1)。
    3. 合理分解:每层加工数建议控制在7±2个。

5. 完整案例:在线购书系统DFD

(1)第一步:顶层DFD(0层)—— 定系统边界

graph TD
    %% 外部实体
    Customer[“顾客”]
    Warehouse[“书店仓库”]
    PaymentGateway[“支付平台”]

    %% 顶层加工(系统整体)
    System(0.0 在线购书系统)

    %% 数据流
    Customer -->|“1. 购书请求<br>(图书ID,数量,地址)”| System
    PaymentGateway -->|“2. 支付确认<br>(订单号,状态)”| System
    System -->|“3. 订单确认<br>(订单号,预计发货时间)”| Customer
    System -->|“4. 发货通知<br>(订单号,图书清单,地址)”| Warehouse

要点:明确系统与外部世界的所有接口。

(2)第二步:一级DFD —— 分解核心流程

graph TD
    %% 外部实体
    C[“顾客”]
    W[“书店仓库”]
    PG[“支付平台”]

    %% 一级子加工
    P1(1.0 订单处理)
    P2(2.0 库存检查)
    P3(3.0 支付验证)
    P4(4.0 订单管理与通知)

    %% 数据存储
    DS1[“F1 图书库存表”]
    DS2[“F2 用户订单表”]

    %% 外部→加工
    C -->|“购书请求”| P1
    PG -->|“支付确认”| P3

    %% 加工→加工
    P1 -->|“库存查询请求”| P2
    P2 -->|“库存查询结果”| P1
    P1 -->|“待支付订单”| P3
    P3 -->|“支付验证结果”| P4

    %% 加工↔数据存储
    P2 <-->|“读/更新库存”| DS1
    P4 <-->|“读/写订单”| DS2

    %% 加工→外部
    P4 -->|“订单确认”| C
    P4 -->|“发货通知”| W

要点:展示系统内部核心处理流程和数据存储,保持与顶层图的输入输出平衡。

(3)第三步:二级DFD —— 细化“1.0 订单处理”

graph TD
    %% 外部实体
    C[顾客]
    
    %% 父加工边界(子图)
    subgraph 1_0_订单处理["1.0 订单处理"]
        direction LR
        SP1[1.1 接收与校验请求]
        SP2[1.2 验证用户信息]
        SP3[1.3 生成订单草稿]
    end
    
    %% 关联的加工和数据存储(本图外部)
    P2(2.0 库存检查)
    DS3[F3 用户信息表]
    P3_Placeholder(3.0 支付验证)
    
    %% 数据流
    C -->|购书请求| SP1
    SP1 -->|校验后的请求| SP2
    SP2 <-->|查询用户| DS3
    SP2 -->|有效用户信息| SP3
    SP3 -->|库存查询请求| P2
    P2 -->|库存结果| SP3
    SP3 -->|待支付订单| P3_Placeholder

要点:展示单一加工的详细处理步骤,保持与父加工(1.0)的输入输出平衡。

(四)数据字典(DD)详解与示例

1. 定义与作用

  • 对系统中所有数据元素的有组织列表及精确定义。
  • 与DFD共同构成完整的系统逻辑模型,是需求规格说明书的核心。

2. 核心条目及描述要素

条目类型 关键描述要素
数据流 名称、说明、来源、去向、组成(数据结构)、流量
数据元素 名称、类型、长度、取值范围、相关元素
数据文件 名称、简述、输入/输出数据、组成、存储方式、存取频率
加工逻辑 名称、编号、简要描述、输入/输出、加工规则
源点/汇点 名称、简述、相关数据流

3. 数据定义式符号

符号 含义 示例
= 定义为 订单编号 = 10{数字}10
+ 与(顺序连接) 购书请求 = 用户ID + 图书列表 + 收货地址
{ } 重复(循环) 图书列表 = 1{图书ID + 数量}10
\[ \| \] 或(选择) 支付状态 = [ “成功” \| “失败” \| “待处理” ]
( ) 任选(可选) 发票信息 = (公司名称 + 税号)
m..n 界域(次数/值域) 数量 = 1..999
“ ” 基本数据元素 用户ID = “字符串”

4. 数据字典示例(针对在线购书系统)

// ========== 数据流定义 ==========
1. 数据流名:购书请求
   说明:顾客提交的购买图书的信息集合。
   来源:外部实体“顾客”
   去向:加工“1.1 接收与校验请求”
   组成:用户ID + 图书列表 + 收货地址
   流量:约500份/天,高峰时段100份/小时。

2. 数据流名:图书列表
   组成:1{(图书ID + 数量)}10
   说明:一次请求中最多购买10种图书。

// ========== 数据文件定义 ==========
1. 数据文件:F2 用户订单表
   简述:存储所有生成订单的详细信息。
   输入数据:订单草稿(来自加工1.3)、支付验证结果(来自加工3.0)
   输出数据:订单详情(去向加工4.0)
   组成:订单号 + 用户ID + 订单状态 + 下单时间 + 图书列表 + 总金额 + 收货地址 + (支付单号) + (发货时间)
   存储方式:按订单号索引存储。
   存取频率:非常频繁。

// ========== 数据元素定义 ==========
1. 数据元素:订单号
   类型:字符串
   长度:12位
   格式:“DD”-“YYYYMMDD”-“0000”
   取值范围:系统自动生成,唯一。
   相关文件:F2 用户订单表

2. 数据元素:订单状态
   类型:枚举字符串
   长度:10
   取值:[ “待支付” | “已支付” | “配货中” | “已发货” | “已完成” | “已取消” ]

(五)实体关系图(E-R图)详解与示例

1. 定义与作用

  • 描述现实世界中实体及其关系的图示化概念模型,独立于系统实现。
  • 核心用于数据库概念设计,是数据库逻辑设计的基础。

2. 核心组成成分

成分 定义 表示形式
实体 需维护数据的对象(人、物、事件、概念)。 长方形
属性 实体的命名特性。 椭圆形(连接实体)
联系 实体实例间的关联。 菱形框(连接相关实体)

3. 联系的类型(Cardinality)

  • 1:1(一对一):一个A实体关联一个B实体。
  • 1:N(一对多):一个A实体关联多个B实体。
  • M:N(多对多):多个A实体关联多个B实体。

4. E-R图示例:在线购书系统核心实体关系

erDiagram
    CUSTOMER ||--o{ ORDER : "提交"
    CUSTOMER {
        string customer_id PK "用户ID"
        string name "姓名"
        string phone "电话"
        string address "地址"
    }
    ORDER ||--|{ ORDER_LINE : "包含"
    ORDER {
        string order_id PK "订单号"
        date order_date "下单日期"
        string status "状态"
        decimal total_amount "总金额"
    }
    BOOK ||--o{ ORDER_LINE : "被订购"
    BOOK {
        string book_id PK "图书ID"
        string title "书名"
        string author "作者"
        decimal price "单价"
        int stock "库存量"
    }
    ORDER_LINE {
        int quantity "购买数量"
        decimal subtotal "小计"
    }
    PAYMENT ||--|| ORDER : "支付"
    PAYMENT {
        string payment_id PK "支付单号"
        string method "支付方式"
        decimal amount "支付金额"
        string status "支付状态"
    }

图例说明

  • CUSTOMER(顾客)与 ORDER(订单)是 1对多 关系:一个顾客可以提交多个订单。
  • ORDER(订单)与 ORDER_LINE(订单明细)是 1对多 关系:一个订单包含多条图书购买明细。
  • BOOK(图书)与 ORDER_LINE(订单明细)是 1对多 关系:一种图书可以出现在多个订单明细中。
  • ORDER(订单)与 PAYMENT(支付记录)是 1对1 关系:一个订单对应一次支付记录。
  • ORDER_LINE 是一个关联实体,用于解决ORDERBOOK之间直接的“多对多”关系,并记录“数量”、“小计”等关联属性。

(六)总结:结构化分析建模流程

  1. 需求调研:获取用户初始需求。
  2. 绘制顶层DFD:划定系统范围,识别所有外部实体和输入输出流。
  3. 逐层细化DFD
    • 分解加工,加入数据存储。
    • 始终遵循平衡原则和绘图规则。
    • 分解至功能足够简单、明确为止。
  4. 定义数据字典(DD)
    • 对DFD中出现的所有数据流、数据存储、数据元素进行严格定义。
    • 使用定义式符号清晰描述数据结构。
  5. 绘制E-R图
    • 从DFD和数据字典中识别需要持久化存储的核心数据对象(实体)。
    • 分析实体间的静态业务关系(联系)。
    • 确定联系的种类(1:1, 1:N, M:N)。
  6. 集成与验证
    • 检查DFD、DD、E-R图之间的一致性。
    • 与用户共同评审模型,确保准确反映需求。
    • 形成正式的《软件需求规格说明书(SRS)》。

通过 DFD(功能与流程)、DD(数据定义)、E-R图(数据关系) 这三者的有机结合,结构化分析方法能够从不同维度全面、清晰、无歧义地定义软件系统的需求,为后续的系统设计奠定坚实基础。

二、E-R图中联系的类型详解与示例

在E-R图中,联系(Relationship) 是实体之间的连接或关联。根据实体参与联系的方式和数量,主要有三种基本类型:

(一)三种基本联系类型

1. 一对一联系(1:1)

定义:实体集A中的每个实体至多与实体集B中的一个实体相关联,反之亦然。

表示||---||(双竖线连接)

示例

erDiagram
    STUDENT ||--|| STUDENT_ID_CARD : "拥有"
    STUDENT {
        string student_id PK
        string name
    }
    STUDENT_ID_CARD {
        string card_number PK
        date issue_date
        date expiry_date
    }

业务场景

  • 一个学生只有一张学生证,一张学生证只属于一个学生
  • 一个人只有一个身份证号,一个身份证号只对应一个人
  • 一个公司只有一个法人代表,一个法人代表只代表一个公司

2. 一对多联系(1:N)

定义:实体集A中的每个实体可以与实体集B中的任意多个实体相关联,但实体集B中的每个实体至多与实体集A中的一个实体相关联。

表示||--o{(左边双竖线,右边空心圆+大括号)

示例

erDiagram
    DEPARTMENT ||--o{ EMPLOYEE : "包含"
    DEPARTMENT {
        string dept_id PK
        string dept_name
    }
    EMPLOYEE {
        string emp_id PK
        string emp_name
        string position
    }

业务场景

  • 一个部门有多个员工,一个员工只属于一个部门
  • 一个客户可以有多个订单,一个订单只属于一个客户
  • 一个分类下有多个商品,一个商品只属于一个分类

3. 多对多联系(M:N)

定义:实体集A中的每个实体可以与实体集B中的任意多个实体相关联,反之亦然。

表示}o--o{(两边都是空心圆+大括号)

示例

erDiagram
    STUDENT }o--o{ COURSE : "选修"
    STUDENT {
        string student_id PK
        string name
        string major
    }
    COURSE {
        string course_id PK
        string course_name
        int credits
    }

业务场景

  • 一个学生可以选修多门课程,一门课程可以被多个学生选修
  • 一个作者可以写多本书,一本书可以有多个作者
  • 一个订单可以包含多种商品,一种商品可以在多个订单中

(二)联系的重要概念

1. 联系的度(Degree)

  • 一元联系(递归联系):同一实体集内的实体之间的联系
  • 二元联系:两个实体集之间的联系(最常见)
  • 多元联系:三个或更多实体集之间的联系

2. 参与约束(Participation Constraints)

  • 完全参与:实体集中的每个实体都必须参与联系(用双线表示)
  • 部分参与:实体集中的部分实体参与联系(用单线表示)

示例

erDiagram
    DEPARTMENT ||--|{ EMPLOYEE : "管理"
    DEPARTMENT {
        string dept_id PK
        string dept_name
    }
    EMPLOYEE {
        string emp_id PK
        string emp_name
    }

在这个例子中:

  • ||--表示部门对员工是完全参与(每个部门都有员工)
  • ||{表示员工对部门是部分参与(可能有员工不属于任何部门)

(三)在线购书系统完整E-R图示例

erDiagram
    CUSTOMER {
        string customer_id PK
        string name
        string email
        string address
    }
    
    ORDER {
        string order_id PK
        string customer_id FK
        date order_date
        decimal total_amount
        string status
    }
    
    BOOK {
        string book_id PK
        string title
        string isbn
        decimal price
        int stock
    }
    
    ORDER_LINE {
        string order_id FK
        string book_id FK
        int quantity
        decimal unit_price
        decimal subtotal
    }
    
    CATEGORY {
        string category_id PK
        string name
    }
    
    BOOK_CATEGORY {
        string book_id FK
        string category_id FK
    }
    
    AUTHOR {
        string author_id PK
        string name
        string bio
    }
    
    BOOK_AUTHOR {
        string book_id FK
        string author_id FK
        string role
    }
    
    PAYMENT {
        string payment_id PK
        string order_id FK
        decimal amount
        string method
        string status
    }
    
    SHIPMENT {
        string shipment_id PK
        string order_id FK
        string tracking_no
        string carrier
        date ship_date
    }
    
    WAREHOUSE {
        string warehouse_id PK
        string name
        string location
    }
    
    INVENTORY {
        string book_id FK
        string warehouse_id FK
        int quantity
        int reorder_level
    }
    
    %% 1:1 关系
    ORDER ||--|| PAYMENT : "支付"
    ORDER ||--|| SHIPMENT : "发货"
    
    %% 1:N 关系
    CUSTOMER ||--o{ ORDER : "创建"
    CATEGORY ||--o{ BOOK_CATEGORY : "分类"
    WAREHOUSE ||--o{ INVENTORY : "存放"
    
    %% 通过关联实体表示的 M:N 关系
    BOOK ||--o{ ORDER_LINE : "包含在"
    ORDER ||--o{ ORDER_LINE : "包含"
    
    BOOK ||--o{ BOOK_CATEGORY : "属于"
    BOOK ||--o{ BOOK_AUTHOR : "由"
    AUTHOR ||--o{ BOOK_AUTHOR : "撰写"
    
    BOOK ||--o{ INVENTORY : "存放在"

(四)联系的高级概念

1. 弱实体与强实体

erDiagram
    ORDER ||--||ORDER_LINE : "包含"
    ORDER {
        string order_id PK
        date order_date
    }
    ORDER_LINE {
        int line_no PK
        int quantity
    }

解释

  • 强实体:ORDER,有自己的主键(order_id)
  • 弱实体:ORDER_LINE,依赖于ORDER存在,其主键包含父实体的主键(order_id + line_no)

2. 三元联系

erDiagram
    DOCTOR }o--o{ PATIENT : "诊治"
    DOCTOR }o--o{ MEDICINE : "开具"
    PATIENT }o--o{ MEDICINE : "使用"
    
    DOCTOR {
        string doctor_id PK
        string name
    }
    PATIENT {
        string patient_id PK
        string name
    }
    MEDICINE {
        string medicine_id PK
        string name
    }
    
    PRESCRIPTION {
        datetime prescribe_date
        string dosage "剂量"
        string frequency "频率"
    }

解释:医生、病人、药物之间的三元联系,通过处方(PRESCRIPTION)关联实体实现。

3. 递归联系(一元联系)

erDiagram
    EMPLOYEE }o--o{ EMPLOYEE : "管理"
    EMPLOYEE {
        string emp_id PK
        string name
        string title
    }
    EMPLOYEE_MANAGEMENT {
        string relationship_type "关系类型"
        date effective_date "生效日期"
    }

解释:员工实体集内部的”管理”联系,表示员工之间的上下级关系。

(五)实际设计中的注意事项

  1. 多对多联系的分解:在数据库实现中,M:N联系需要通过关联实体转换为两个1:N联系
  2. 联系属性:当联系本身具有属性时,必须创建关联实体
  3. 参与度约束:明确是完全参与还是部分参与,影响数据完整性和业务规则
  4. 基数约束:除了1:1、1:N、M:N,还可以指定具体数量范围(如1..5)

(六)总结

联系类型 符号表示 数据库实现 示例
1:1 \|\|---\|\| 外键放在任意一方,或合并为一张表 用户-用户档案
1:N \|\|--o{ 外键放在”多”的一方 部门-员工
M:N }o--o{ 创建关联表(连接表) 学生-课程

关键设计原则

  1. 识别正确的联系类型:仔细分析业务规则
  2. 避免过度复杂:尽量使用二元联系,谨慎使用三元以上联系
  3. 考虑性能:联系类型影响查询效率和数据库设计
  4. 保持一致性:在整个E-R图中使用统一的符号和命名约定