当前位置: 首页 > article >正文

UML类图的六大关系:依赖,泛化,实现,关联,聚合,组合

1.依赖关系

依赖关系是 UML 类图中一种相对较弱的关系,用于表明一个类在某种程度上依赖于另一个类来完成特定任务。当类 A 的某个方法使用了类 B 的对象作为参数、局部变量,或是调用了类 B 的静态方法时,就存在类 A 对类 B 的依赖关系。这种关系通常是临时性的,仅在类 A 执行特定操作时才会关联到类 B。

  • 特点
    • 临时性:依赖关系不像关联、聚合、组合那样表示类之间长期稳定的结构联系,它只是方法执行期间的短暂关联。
    • 单向或双向:可以是单向的,即 A 依赖 B 但 B 不依赖 A;也可能是双向的,不过较为少见,意味着双方的方法互相有所依赖。
    • 松散耦合:依赖关系下的两个类耦合度较低,被依赖类的变动对依赖类的影响范围通常局限于使用到的具体方法。
  1. 使用对象作为参数产生依赖
    在这段代码中,ReportWriter类依赖于Printer类。ReportWriter类的generateReport方法需要借助Printer类的printText方法来输出报告内容,Printer类的对象作为参数传入,一旦generateReport方法执行完毕,这种依赖联系在本次操作中就结束了。
    class Printer {
    public:
        void printText(const std::string& text) {
            std::cout << text << std::endl;
        }
    };
    
    class ReportWriter {
    public:
        void generateReport(Printer& printer) {
            std::string report = "This is a sample report";
            printer.printText(report);
        }
    };
  2. 使用局部变量引发依赖
    MathStudent类的doHomework方法依赖Calculator类。在doHomework方法内部,创建了Calculator类的局部变量calc,并使用它的add方法完成计算,计算完成后,calc的生命周期结束,此次依赖关系也随之结束。
    class Calculator {
    public:
        int add(int a, int b) {
            return a + b;
        }
    };
    
    class MathStudent {
    public:
        void doHomework() {
            Calculator calc;
            int result = calc.add(3, 4);
            std::cout << "The result of calculation is: " << result << std::endl;
        }
    };
  3. 调用静态方法形成依赖
    Programmer类的writeCode方法依赖Logger类。通过调用Logger类的静态方法logMessageProgrammer类实现了记录日志的功能,这同样是一种依赖关系,而且由于调用静态方法,甚至不需要创建Logger类的实例。
    class Logger {
    public:
        static void logMessage(const std::string& msg) {
            std::cout << "Log: " << msg << std::endl;
        }
    };
    
    class Programmer {
    public:
        void writeCode() {
            Logger::logMessage("Writing some code...");
        }
    };

2.泛化关系

 

泛化关系(Generalization)在 UML 类图里体现的是一种继承关系,它遵循 “is-a” 原则,也就是子类是父类的一种特殊形式。通过泛化,子类能够继承父类的属性和方法,同时还能添加自身独有的属性与方法,实现代码复用与功能扩展。

 
  • 特点
    • 代码复用:父类定义了通用的属性与行为,子类无需重复编写,直接继承即可使用,节省开发精力。例如,若多个子类都需要某个共有的基础方法,在父类中定义一次就行。
    • 层次结构:构建起清晰的类层次体系,便于组织和管理复杂的类关系。比如在图形绘制系统中,各种具体图形类从通用的 “图形” 父类派生,逻辑清晰。
    • 多态支持:结合虚函数机制,子类能重写父类方法,让程序运行时基于对象实际类型来动态调用合适的方法,增强程序灵活性。
       
// 定义父类:动物类Animal
class Animal {
public:
    std::string name;
    Animal(const std::string& n) : name(n) {}

    virtual void makeSound() {
        std::cout << name << " makes a sound" << std::endl;
    }
};

// 定义子类:狗类Dog,继承自Animal类
class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {}

    void makeSound() override {
        std::cout << name << " barks" << std::endl;
    }
};

// 定义子类:猫类Cat,继承自Animal类
class Cat : public Animal {
public:
    Cat(const std::string& n) : Animal(n) {}

    void makeSound() override {
        std::cout << name << " meows" << std::endl;
    }
};
// 图形基类
class Shape {
protected:
    int color;
public:
    Shape(int c) : color(c) {}
    virtual void draw() = 0;
    virtual double getArea() = 0;
};

// 圆形,继承自 Shape
class Circle : public Shape {
private:
    double radius;
public:
    Circle(int c, double r) : Shape(c), radius(r) {}
    void draw() override {
        std::cout << "Drawing a circle with color " << color << std::endl;
    }
    double getArea() override {
        return 3.14 * radius * radius;
    }
};

// 矩形,继承自 Shape
class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(int c, double w, double h) : Shape(c), width(w), height(h) {}
    void draw() override {
        std::cout << "Drawing a rectangle with color " << color << std::endl;
    }
    double getArea() override {
        return width * height;
    }
};
// 交通工具基类
class Vehicle {
protected:
    int speed;
    int wheels;
public:
    Vehicle(int s, int w) : speed(s), wheels(w) {}
    virtual void move() = 0;
};

// 汽车,继承自 Vehicle
class Car : public Vehicle {
public:
    Car(int s, int w) : Vehicle(s, w) {}
    void move() override {
        std::cout << "The car is moving at speed " << speed << " with " << wheels << " wheels." << std::endl;
    }
};

// 自行车,继承自 Vehicle
class Bicycle : public Vehicle {
public:
    Bicycle(int s, int w) : Vehicle(s, w) {}
    void move() override {
        std::cout << "The bicycle is moving at speed " << speed << " with " << wheels << " wheels." << std::endl;
    }
};
// 员工基类
class Employee {
protected:
    std::string name;
    int salary;
public:
    Employee(const std::string& n, int s) : name(n), salary(s) {}
    virtual void work() = 0;
};

// 程序员,继承自 Employee
class Programmer : public Employee {
public:
    Programmer(const std::string& n, int s) : Employee(n, s) {}
    void work() override {
        std::cout << name << " is coding." << std::endl;
    }
};

// 销售,继承自 Employee
class Salesperson : public Employee {
public:
    Salesperson(const std::string& n, int s) : Employee(n, s) {}
    void work() override {
        std::cout << name << " is selling." << std::endl;
    }
};

 

3.实现关系

 

在 UML 类图里,实现关系指的是类与接口之间的一种契约关系。接口定义了一组抽象的操作(方法),而实现类负责为这些抽象操作提供具体的实现代码,以此来满足接口所设定的行为规范。这种关系让程序具备更好的灵活性与可扩展性,不同的实现类能够替换使用,只要它们遵循相同的接口标准。

 
  • 特点
    • 抽象性与具体性结合:接口作为抽象的概念,仅勾勒行为轮廓,不涉及具体实现细节;实现类则把这些抽象方法落地,化为真实可执行的代码。
    • 多态支持:基于实现关系,程序能利用多态特性,通过接口类型的指针或引用,调用不同实现类的对应方法,增强代码灵活性,使程序可以动态适应变化。
    • 解耦与可替换性:依赖接口编程,而非依赖具体类,能降低类与类之间的耦合度。当需要更换功能实现时,只要新的实现类遵循原接口,就能无缝替换,无需大面积改动代码。
// 定义接口类:可绘制接口 Drawable
class Drawable {
public:
    virtual void draw() = 0;
};

// 定义实现类:圆形类 Circle,实现 Drawable 接口
class Circle : public Drawable {
public:
    void draw() override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

// 定义实现类:矩形类 Rectangle,实现 Drawable 接口
class Rectangle : public Drawable {
public:
    void draw() override {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};
// 图形渲染接口
class GraphicRenderer {
public:
    virtual void render() = 0;
    virtual ~GraphicRenderer() = default;
};

// OpenGL 渲染实现类
class OpenGLRenderer : public GraphicRenderer {
public:
    void render() override {
        std::cout << "Rendering using OpenGL" << std::endl;
    }
};

// Vulkan 渲染实现类
class VulkanRenderer : public GraphicRenderer {
public:
    void render() override {
        std::cout << "Rendering using Vulkan" << std::endl;
    }
};
// 数据持久化接口
class DataPersistence {
public:
    virtual void saveData(const std::string& data) = 0;
    virtual std::string loadData() = 0;
    virtual ~DataPersistence() = default;
};

// 文件系统持久化实现类
class FileSystemPersistence : public DataPersistence {
public:
    void saveData(const std::string& data) override {
        std::ofstream file("data.txt");
        if (file.is_open()) {
            file << data;
            file.close();
        }
    }
    std::string loadData() override {
        std::ifstream file("data.txt");
        std::string result;
        if (file.is_open()) {
            std::getline(file, result);
            file.close();
        }
        return result;
    }
};

// 数据库持久化实现类
class DatabasePersistence : public DataPersistence {
public:
    void saveData(const std::string& data) override {
        // 假设这里有数据库连接和插入数据的代码
        std::cout << "Saving data to database: " << data << std::endl;
    }
    std::string loadData() override {
        // 假设这里有数据库查询和获取数据的代码
        return "Data from database";
    }
};
// 排序算法接口
class Sorter {
public:
    virtual void sort(std::vector<int>& data) = 0;
    virtual ~Sorter() = default;
};

// 冒泡排序实现类
class BubbleSorter : public Sorter {
public:
    void sort(std::vector<int>& data) override {
        int n = data.size();
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (data[j] > data[j + 1]) {
                    std::swap(data[j], data[j + 1]);
                }
            }
        }
    }
};

// 快速排序实现类
class QuickSorter : public Sorter {
public:
    void sort(std::vector<int>& data) override {
        // 快速排序的具体实现代码
        // 此处省略完整代码
        std::cout << "Sorting data with Quick Sort" << std::endl;
    }
};

 

4.关联关系

 

在 UML 类图里,关联关系用于表示类与类之间存在某种语义上的连接,这种连接通常意味着它们之间有稳定且长期的交互。关联关系可以是单向的,也可以是双向的,并且常带有多重性(Multiplicity)标识,用来表明一个类的对象与另一个类的对象之间数量上的对应关系。与依赖关系相比,关联关系更为紧密,它不是临时性的方法调用,而是类结构层面的一种联系。

 
  • 特点
    • 稳定性:关联关系描述的是类之间相对持久的关系,不像依赖关系那样只是在方法执行期间短暂出现。例如,在电商系统里,顾客和订单之间的关系就是长期稳定的,顾客长期会下订单,订单长期关联特定顾客。
    • 方向性:可以是单向关联,即一个类知晓另一个类,但反之不然;也能是双向关联,意味着双方类都清楚彼此的存在,能够互相访问。
    • 多重性:多重性用数字、范围或者特定符号(比如 “*” 表示多个 )在关联线上标注,精准定义两个类对象间数量对应,像一个班级有多个学生,一个学生只属于一个班级。
       
  1. 单向关联
    在这段代码中,Employee 类与 Company 类是单向关联关系,Employee 类持有指向 Company 类的指针,员工知晓自己所属的公司,能调用公司相关方法(如获取公司名 ),但公司类并没有反向关联员工类的设计。
    class Company;
    
    // 员工类
    class Employee {
    private:
        Company* employer;
    public:
        Employee(Company* c) : employer(c) {}
        void displayCompany() {
            if (employer!= nullptr) {
                std::cout << "Works for: " << employer->getName() << std::endl;
            }
        }
    };
    
    // 公司类
    class Company {
    private:
        std::string name;
    public:
        Company(const std::string& n) : name(n) {}
        std::string getName() {
            return name;
        }
    };
  2. 双向关联
    这里,Student 类和 Teacher 类呈现双向关联。学生类持有教师类的指针向量,方便添加、罗列自己的教师;教师类也持有学生类的指针向量,能添加学生,双方类都对彼此存在关联,体现了较为紧密的长期互动关系。
    class Teacher;
    
    // 学生类
    class Student {
    private:
        std::vector<Teacher*> teachers;
    public:
        void addTeacher(Teacher* t) {
            teachers.push_back(t);
        }
        void listTeachers() {
            for (auto teacher : teachers) {
                std::cout << "Has teacher: " << teacher->getName() << std::endl;
            }
        }
    };
    
    // 教师类
    class Teacher {
    private:
        std::vector<Student*> students;
    public:
        void addStudent(Student* s) {
            students.push_back(s);
        }
        std::string getName() {
            return "Teacher Name";
        }
    };
  3. 带多重性的关联
    在 UML 类图里,Team 类与 Player 类的关联线旁可标注 “1..*”,意味着一个球队有 1 个到多个球员 ,在代码里用std::vector<Player*>实现了这种一对多的数量对应关系,表明了类之间基于数量的关联特性。
    // 球队类
    class Team {
    private:
        std::vector<Player*> players;
    public:
        void addPlayer(Player* p) {
            players.push_back(p);
        }
    };
    
    // 球员类
    class Player {
    };

 

5.聚合关系

 

聚合关系是 UML 类图里表示整体与部分关系的一种,它属于特殊的关联关系。在聚合关系中,部分能够独立于整体存在,整体和部分有着各自的生命周期。整体对象包含对部分对象的引用,不过,部分对象的所有权不完全归属于整体对象,也就是说,即使整体被销毁了,部分依然可以存活,并且能被其他整体对象复用。

 
  • 特点
    • 弱拥有关系:整体 “拥有” 部分,但这种拥有比较松散,部分并不紧密依附于特定的整体。例如,一台电脑有一个硬盘,硬盘坏了可以拆下来装到别的电脑上,硬盘有独立于这台电脑的生存能力。
    • 独立生命周期:部分对象在整体对象创建之前可以已经存在,在整体对象销毁之后也能继续留存,二者生命周期没有强绑定。
    • 可替换性:由于部分的独立性,在需要的时候,很容易将某个部分从一个整体中拆卸下来,替换成其他同类型的部分。
// 定义部分类:发动机类 Engine
class Engine {
public:
    void start() {
        std::cout << "Engine started." << std::endl;
    }
};

// 定义整体类:汽车类 Car
class Car {
private:
    Engine* engine;
public:
    Car(Engine* e) : engine(e) {}
    ~Car() {
        // 汽车销毁时,不负责销毁发动机
        // 因为发动机有自己独立的生命周期
    }
    void drive() {
        if (engine!= nullptr) {
            engine->start();
            std::cout << "Car is driving." << std::endl;
        }
    }
};
// 定义球员类
class Player {
private:
    std::string name;
public:
    Player(const std::string& n) : name(n) {}
    void play() {
        std::cout << name << " is playing." << std::endl;
    }
};

// 定义球队类
class Team {
private:
    std::vector<Player*> players;
public:
    void addPlayer(Player* p) {
        players.push_back(p);
    }

    ~Team() {
        // 球队解散时,球员不会随之消失
        // 他们可以转会到其他球队
        for (auto player : players) {
            delete player;
        }
        players.clear();
    }

    void playGame() {
        for (auto player : players) {
            player->play();
        }
        std::cout << "The team is playing a game." << std::endl;
    }
};
// 定义书籍类
class Book {
private:
    std::string title;
public:
    Book(const std::string& t) : title(t) {}
    std::string getTitle() {
        return title;
    }
};

// 定义图书馆类
class Library {
private:
    std::vector<Book*> books;
public:
    void addBook(Book* b) {
        books.push_back(b);
    }

    ~Library() {
        // 图书馆关闭时,书籍不会被销毁
        // 它们可以被转移到其他图书馆
        for (auto book : books) {
            delete book;
        }
        books.clear();
    }

    void listBooks() {
        for (auto book : books) {
            std::cout << book->getTitle() << std::endl;
        }
    }
};

 

6.组合关系

 

组合关系同样用于表示整体与部分的关系,但相较于聚合关系,它是一种更强的 “拥有” 形式。在组合关系里,部分不能脱离整体而独立存在,整体对象与部分对象的生命周期紧密绑定,当整体被销毁时,部分对象必然会随之销毁。组合关系体现了一种强内聚性,强调部分是整体不可或缺的一部分。

 
  • 特点
    • 强依赖:部分对象完全依赖于整体对象,离开整体,部分就失去了存在的意义。例如,在人体中,心脏是人体的一部分,脱离了人体,心脏就无法维持正常功能。
    • 同步生命周期:整体对象创建时,部分对象通常也随之创建;整体对象销毁时,部分对象会被自动销毁,它们的生命周期完全同步。
    • 紧密耦合:整体与部分之间耦合紧密,这意味着对整体或部分的修改,可能会较大程度影响另一方,不过也保证了数据的一致性和完整性。
// 定义部分类:心脏类 Heart
class Heart {
public:
    void beat() {
        std::cout << "Heart is beating." << std::endl;
    }
};

// 定义整体类:人类 Human
class Human {
private:
    Heart heart;
public:
    Human() {}
    ~Human() {
        // 当人类对象被销毁时,心脏对象也自动销毁
        // 无需额外处理
    }
    void live() {
        heart.beat();
        std::cout << "Human is living." << std::endl;
    }
};
// 按钮类
class Button {
public:
    void click() {
        std::cout << "Button is clicked." << std::endl;
    }
};

// 窗口类
class Window {
private:
    std::vector<Button> buttons;
public:
    Window() {
        // 初始化时创建按钮
        Button closeButton;
        Button minimizeButton;
        buttons.push_back(closeButton);
        buttons.push_back(minimizeButton);
    }
    void handleEvents() {
        for (auto& button : buttons) {
            button.click();
        }
    }
    ~Window() {
        // 当窗口被销毁时,按钮也随之销毁
        // 因为按钮是窗口的一部分,不能独立存在于这个窗口系统之外
    }
};
// 书页类
class Page {
private:
    std::string content;
public:
    Page(const std::string& c) : content(c) {}
    std::string getContent() {
        return content;
    }
};

// 书类
class Book {
private:
    std::vector<Page> pages;
public:
    Book() {
        // 初始化书页
        Page p1("This is the content of page 1.");
        Page p2("This is the content of page 2.");
        pages.push_back(p1);
        pages.push_back(p2);
    }
    void displayPages() {
        for (auto& page : pages) {
            std::cout << page.getContent() << std::endl;
        }
    }
    ~Book() {
        // 当书被销毁时,书页也随之销毁
        // 书页不能脱离书而独立存在
    }
};
// 发动机类
class Engine {
public:
    void start() {
        std::cout << "Engine is starting." << std::endl;
    }
};

// 汽车类
class Car {
private:
    Engine engine;
public:
    Car() {}
    void drive() {
        engine.start();
        std::cout << "The car is driving." << std::endl;
    }
    ~Car() {
        // 当汽车被销毁时,发动机也自动销毁
        // 因为发动机是汽车的一部分,不能独立存在
    }
};


http://www.kler.cn/a/461702.html

相关文章:

  • 云从科技Java面试题及参考答案
  • C语言带参数的宏定义的相关知识汇总(最常用的形式、带标记分隔符##的形式...)
  • 普及组集训数据结构--并查集
  • 日志聚类算法 Drain 的实践与改良
  • 4.Web安全——JavaScript基础
  • TP8 前后端跨域访问请求API接口解决办法
  • 使用Python实现基因组数据分析:探索生命的奥秘
  • 免押租赁系统助力共享经济发展新模式
  • 【JAVA】神经网络的基本结构和前向传播算法
  • WebAssembly 学习笔记
  • 网络安全 | 5G网络安全:未来无线通信的风险与对策
  • OpenVPN 被 Windows 升级破坏
  • Linux命令——3.网络与用户
  • SQL常用语句(基础)大全
  • C++算法20例
  • Listwise 模型时间线梳理
  • Flask是什么?深入解析 Flask 的设计与应用实践
  • main函数
  • Kafka优势剖析-顺序写、零拷贝
  • 【C++】22___STL常用算法
  • 【每日学点鸿蒙知识】导入cardEmulation、自定义装饰器、CallState状态码顺序、kv配置、签名文件配置
  • node.js之---集群(Cluster)模块
  • 最新版Chrome浏览器加载ActiveX控件之CFCA安全输入控件
  • 设置虚拟机设备的dp和pt
  • 07-ArcGIS For JavaScript--隐藏参数qualitySettings(memory和lod控制)
  • DataV数据可视化