UML依赖关系详解

1. 概述

在统一建模语言(UML)中,依赖关系是一种重要的模型元素,用来表示一个事物(比如类、组件或包)依赖于另一个事物的情况。依赖关系通常表示一个事物的定义或实现部分地或完全依赖于另一个事物。

2. 特点

  • 方向性:依赖关系是有方向的,表示一个元素使用或依赖另一个元素。
  • 动态性:依赖通常表示在运行时或在编译时的关系,不像关联那样通常表示更长久的关系。
  • 弱关系:相比于类的关联、聚合或组合,依赖是一种相对较弱的关系,通常用于表示非永久性的使用或交互。

3. 依赖关系的类型

UML 依赖关系可以细分为以下几种类型:

3.1 使用依赖

一个类(客户端)在其方法中使用另一个类(供应商)的实例。例如:

  • 一个 Car 类使用 Engine 类来提供动力。
#include <iostream>class Engine {
public:void start() {std::cout << "Engine started" << std::endl;}void stop() {std::cout << "Engine stopped" << std::endl;}
};class Car {
public:Car() {engine = new Engine();}void start() {engine->start();}void stop() {engine->stop();}private:Engine* engine;
};int main() {Car car;car.start();car.stop();return 0;
}
  • 一个 ReportGenerator 类使用 DataSource 类来获取数据。
#include <iostream>
#include <vector>class DataSource {
public:virtual ~DataSource() {}virtual std::vector<int> getData() = 0;
};class JdbcDataSource : public DataSource {
public:std::vector<int> getData() override {std::cout << "Getting data from JDBC data source" << std::endl;return {1, 2, 3};}
};class CsvDataSource : public DataSource {
public:std::vector<int> getData() override {std::cout << "Getting data from CSV data source" << std::endl;return {4, 5, 6};}
};class ReportGenerator {
public:ReportGenerator(DataSource* dataSource) {this->dataSource = dataSource;}void generateReport() {std::vector<int> data = dataSource->getData();// ... 生成报表 ...}private:DataSource* dataSource;
};int main() {// 使用 JDBC 数据源JdbcDataSource* jdbcDataSource = new JdbcDataSource();ReportGenerator reportGenerator1(jdbcDataSource);reportGenerator1.generateReport();// 使用 CSV 数据源CsvDataSource* csvDataSource = new CsvDataSource();ReportGenerator reportGenerator2(csvDataSource);reportGenerator2.generateReport();return 0;
}

3.2 创建依赖

一个类负责创建另一个类的实例。例如:

  • 一个 Factory 类负责创建 Product 类的实例。
#include <iostream>class Product {
public:virtual ~Product() {}virtual void doSomething() = 0;
};class ConcreteProductA : public Product {
public:void doSomething() override {std::cout << "ConcreteProductA do something" << std::endl;}
};class ConcreteProductB : public Product {
public:void doSomething() override {std::cout << "ConcreteProductB do something" << std::endl;}
};class Factory {
public:virtual ~Factory() {}virtual Product* createProduct() = 0;
};class ConcreteFactoryA : public Factory {
public:Product* createProduct() override {return new ConcreteProductA();}
};class ConcreteFactoryB : public Factory {
public:Product* createProduct() override {return new ConcreteProductB();}
};int main() {Factory* factory = new ConcreteFactoryA();Product* productA = factory->createProduct();productA->doSomething();factory = new ConcreteFactoryB();Product* productB = factory->createProduct();productB->doSomething();return 0;
}
  • 一个 Document 类负责创建 Paragraph 类的实例。
#include <iostream>
#include <vector>class Section {
public:Section(const std::string& text) {this->text = text;}void print() {std::cout << text << std::endl;}private:std::string text;
};class Document {
public:void addSection(const std::string& text) {sections.push_back(new Section(text));}void print() {for (Section* section : sections) {section->print();}}private:std::vector<Section*> sections;
};int main() {Document document;document.addSection("This is the first section.");document.addSection("This is the second section.");document.print();return 0;
}

3.3 参数依赖

一个类的方法接受另一个类的实例作为参数。例如:

  • 一个 FileReader 类的方法接受一个 File 类的实例作为参数。
#include <iostream>
#include <fstream>class File {
public:File(const std::string& path) {this->path = path;}const std::string& getPath() const {return path;}private:std::string path;
};class FileReader {
public:void readFile(const File& file) {std::ifstream ifs(file.getPath());if (ifs.is_open()) {std::string line;while (getline(ifs, line)) {std::cout << line << std::endl;}ifs.close();} else {std::cout << "Error opening file" << std::endl;}}
};int main() {File file("C:/test.txt");FileReader fileReader;fileReader.readFile(file);return 0;
}
  • 一个 List 类的 Add 方法接受一个要添加的元素作为参数。
#include <iostream>
#include <vector>template <typename T>
class List {
public:void add(const T& element) {elements.push_back(element);}void print() {for (const T& element : elements) {std::cout << element << " ";}std::cout << std::endl;}private:std::vector<T> elements;
};int main() {List<int> list;list.add(1);list.add(2);list.add(3);list.print();return 0;
}

3.4 返回值依赖

一个类的方法返回另一个类的实例。例如:

  • 一个 Database 类的方法返回一个 Connection 类的实例。
#include <iostream>class Connection {
public:Connection() {std::cout << "Creating connection" << std::endl;}~Connection() {std::cout << "Closing connection" << std::endl;}
};class Database {
public:Connection* getConnection() {return new Connection();}
};int main() {Database database;Connection* connection = database.getConnection();// 使用连接...delete connection;return 0;
}
  • 一个 Parser 类的方法返回一个 Document 类的实例。
#include <iostream>
#include <fstream>class Document {
public:Document(const std::string& text) {this->text = text;}void print() {std::cout << text << std::endl;}private:std::string text;
};class Parser {
public:Document* parse(const std::string& filePath) {std::ifstream ifs(filePath);if (ifs.is_open()) {std::string text;while (getline(ifs, text)) {// ... 解析文本 ...}ifs.close();return new Document(text);} else {std::cout << "Error opening file" << std::endl;return nullptr;}}
};int main() {Parser parser;Document* document = parser.parse("C:/test.txt");if (document != nullptr) {document->print();delete document;}return 0;
}

3.6 绑定依赖

指模板实例化时,将模板类绑定到具体的参数上。

类模板是指可以生成不同类型对象的类。类模板的实例化需要指定具体的类型参数。如果一个类的模板实例化依赖于另一个类,那么就表示该类模板依赖于另一个类。例如:

  • 一个 List 类模板可以生成不同类型元素的列表。
#include <iostream>
#include <vector>template <typename T>
class List {
public:void add(const T& element) {elements.push_back(element);}void print() {for (const T& element : elements) {std::cout << element << " ";}std::cout << std::endl;}private:std::vector<T> elements;
};int main() {// 生成 int 型元素的列表List<int> list1;list1.add(1);list1.add(2);list1.add(3);list1.print();// 生成 string 型元素的列表List<std::string> list2;list2.add("Hello");list2.add("World");list2.add("!");list2.print();return 0;
}
  • 一个 Map 类模板可以生成不同类型键值对的映射。
#include <iostream>
#include <map>template <typename K, typename V>
class Map {
public:void add(const K& key, const V& value) {elements[key] = value;}void print() {for (const auto& pair : elements) {std::cout << pair.first << " -> " << pair.second << std::endl;}}private:std::map<K, V> elements;
};int main() {// 生成 int 型键值对的映射Map<int, int> map1;map1.add(1, 10);map1.add(2, 20);map1.add(3, 30);map1.print();// 生成 string 型键值对的映射Map<std::string, std::string> map2;map2.add("Hello", "World");map2.add("Goodbye", "Cruel World");map2.print();return 0;
}

3.7 实现依赖

实现关系是指一个类(实现类)承诺实现另一个类(接口)定义的契约。接口定义了一组方法和属性,但并不提供具体的实现,而是描述了一种行为规范。实现类则提供了这些方法和属性的具体实现。例如:

  • 一个 Animal 类定义一个 Speak 接口。
  • 一个 Dog 类实现 Animal 接口,并提供具体的 Speak 方法实现。
#include <iostream>class Animal {
public:virtual void speak() = 0; // 纯虚函数,没有具体实现
};class Dog : public Animal {
public:void speak() override {std::cout << "Woof!" << std::endl;}
};int main() {Dog dog;dog.speak();return 0;
}

实现关系的特点:

  • 契约: 接口定义了一种行为规范,实现类必须遵守该规范。
  • 实现: 实现类提供接口定义的方法和属性的具体实现。
  • 依赖: 实现类依赖于接口,因为需要根据接口定义来实现其功能。
  • 多态: 接口可以被多个类实现,从而提供不同的实现方式。

3.8 扩展依赖

扩展关系是一种 UML 关系类型,表示一个用例(扩展用例)在某些条件下扩展另一个用例(基本用例)的功能。扩展关系通常用于表示可选的、非必须的功能。

扩展关系的关键点:

  • 扩展点: 基本用例中定义的点,扩展用例可以在该点插入其行为。
  • 可选性: 扩展用例不是基本用例必需的,只有在满足特定条件时才会执行。
  • 行为插入: 扩展用例的行为插入到基本用例的流程中,修改或补充基本用例的行为。
  • 类型: 扩展关系可以是包含扩展两种类型。

扩展关系的应用场景:

  • 可选功能: 为基本用例添加可选功能,例如日志记录、错误处理等。
  • 条件性功能: 仅在满足特定条件时才执行的功能,例如权限控制、数据验证等。
  • 行为变异: 在不同情况下对基本用例行为进行不同的变异,例如不同的用户界面、不同的处理流程等。

扩展关系的优点

  • 灵活性: 扩展关系可以使代码更加灵活,可以根据需要动态添加或删除功能。
  • 可扩展性: 扩展关系可以使代码更加可扩展,可以方便地添加新的功能。
  • 可维护性: 扩展关系可以使代码更加模块化和可维护性,可以将可选功能或条件性功能从基本用例中分离出来。

扩展关系与其他关系的比较:

  • 包含关系: 包含关系表示一个用例包含另一个用例的所有功能,扩展关系则表示扩展用例只在某些情况下扩展基本用例的部分功能。
  • 继承关系: 继承关系表示一个类继承另一个类的所有属性和方法,扩展关系则表示两个用例之间是一种依赖关系。

扩展关系示例:

  • 一个 登录 用例可以扩展一个 安全检查 用例,在需要进行安全检查时才执行安全检查功能。
  • 一个 下单 用例可以扩展一个 优惠计算 用例,在满足优惠条件时才计算优惠价格。
  • 一个文件上传用例可以扩展一个病毒扫描用例,在上传文件之前进行病毒扫描。
#include <iostream>class IControl {
public:virtual void Click() = 0;protected:virtual void DoClick() = 0;
};class Button : public IControl {
public:void Click() {std::cout << "按钮被点击了!" << std::endl;DoClick();}protected:void DoClick() override {std::cout << "按钮执行点击操作!" << std::endl;}
};class Security {
public:virtual bool VerifyUser() = 0;protected:virtual void DoVerifyUser() = 0;
};class LoginUseCase {
public:void Execute() {std::cout << "登录用例正在执行..." << std::endl;// ...// 检查是否需要安全检查。if (isSecurityCheckNeeded) {Security security;if (!security.VerifyUser()) {std::cout << "登录失败:身份验证失败!" << std::endl;return;}}// ...}private:bool isSecurityCheckNeeded = true;
};int main() {LoginUseCase loginUseCase;loginUseCase.Execute();return 0;
}

Control 类

  • Control 类是一个抽象类,代表一种控件。
  • Control 类定义了一个虚方法 Click(),用于触发控件的点击操作。

Button 类

  • Button 类继承自 Control 类,代表一种按钮控件。
  • Button 类实现了 Click() 方法,用于执行按钮的点击操作。
  • 当用户点击按钮时,Button 类会执行 Click() 方法,并触发相应的业务逻辑。

Security 类

  • Security 类继承自 Control 类,代表一种安全控制。
  • Security 类没有实现 Click() 方法,因为它不是用于点击操作的控件。
  • Security 类可以提供其他方法,用于执行安全检查操作。

LoginUseCase 类

  • LoginUseCase 类是一个用例,代表登录用例。
  • LoginUseCase 类使用 Button 类和 Security 类来实现其功能。
  • LoginUseCase 类根据需要动态添加或删除 Button 类和 Security 类。

#include <iostream>class Order {
public:virtual double calculatePrice() = 0; // 纯虚函数,没有具体实现protected:double originalPrice;public:Order(double originalPrice) {this->originalPrice = originalPrice;}
};class DiscountCalculator {
public:virtual bool isEligibleForDiscount(const Order& order) = 0; // 纯虚函数,没有具体实现virtual double calculateDiscount(const Order& order) = 0; // 纯虚函数,没有具体实现
};class OrderWithDiscount : public Order {
public:OrderWithDiscount(double originalPrice) : Order(originalPrice) {}double calculatePrice() override {DiscountCalculator calculator;if (calculator.isEligibleForDiscount(*this)) {return originalPrice - calculator.calculateDiscount(*this);} else {return originalPrice;}}
};class SimpleDiscountCalculator : public DiscountCalculator {
public:bool isEligibleForDiscount(const Order& order) override {return order.originalPrice >= 100;}double calculateDiscount(const Order& order) override {return order.originalPrice * 0.1;}
};int main() {Order* order = new OrderWithDiscount(120);std::cout << "原价:" << order->originalPrice << std::endl;std::cout << "优惠价:" << order->calculatePrice() << std::endl;return 0;
}

在这个例子中,OrderWithDiscount 类扩展了 Order 类,并使用了 DiscountCalculator 类来计算优惠价格。


#include <iostream>
#include <fstream>class File {
public:virtual bool isInfected() = 0; // 纯虚函数,没有具体实现protected:std::string path;public:File(const std::string& path) {this->path = path;}
};class VirusScanner {
public:virtual bool scan(const File& file) = 0; // 纯虚函数,没有具体实现
};class FileUpload {
public:virtual void upload(const File& file) = 0; // 纯虚函数,没有具体实现
};class FileUploadWithVirusScan : public FileUpload {
public:FileUploadWithVirusScan() {}void upload(const File& file) override {VirusScanner scanner;if (!scanner.scan(file)) {std::cout << "文件 " << file.path << " 含有病毒,无法上传" << std::endl;return;}// 上传文件...std::cout << "文件 " << file.path << " 上传成功" << std::endl;}
};class SimpleVirusScanner : public VirusScanner {
public:bool scan(const File& file) override {std::ifstream ifs(file.path);if (ifs.is_open()) {// 扫描文件内容...return true;} else {return false;}}
};int main() {File* file = new File("C:/test.txt");FileUploadWithVirusScan uploader;uploader.upload(*file);return 0;
}

在这个例子中,FileUploadWithVirusScan 类扩展了 FileUpload 类,并使用了 VirusScanner 类来扫描文件是否含有病毒。

4. 依赖关系的表示

假设有一个ReportGenerator类,它负责生成报告,并使用DataSource类来获取所需的数据。在这种情况下,ReportGenerator依赖于DataSource,因为它需要DataSource提供的数据来完成其功能。在UML图中,这种依赖关系会用一条从ReportGenerator指向DataSource的带有开放箭头的虚线来表示。

5. 注意事项

  • 依赖关系应该尽可能地弱,以便提高代码的模块性和可维护性。
  • 循环依赖关系应该避免,因为它会导致代码难以理解和维护。
  • 依赖关系应该在设计阶段仔细考虑,并进行必要的调整和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/708519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVPR2024|AIGC(图像生成,视频生成等)相关论文汇总(附论文链接/开源代码/解析)【持续更新】

CVPR2024&#xff5c;AIGC相关论文汇总&#xff08;如果觉得有帮助&#xff0c;欢迎点赞和收藏&#xff09; Awesome-CVPR2024-AIGC1.图像生成(Image Generation/Image Synthesis)ECLIPSE: A Resource-Efficient Text-to-Image Prior for Image GenerationsInstanceDiffusion: …

机器人内部传感器阅读梳理及心得-速度传感器-模拟式速度传感器

速度传感器是机器人内部传感器之一&#xff0c;是闭环控制系统中不可缺少的重要组成部分&#xff0c;它用来测量机器人关节的运动速度。可以进行速度测量的传感器很多&#xff0c;如进行位置测量的传感器大多可同时获得速度的信息。但是应用最广泛、能直接得到代表转速的电压且…

fastjson序列化MessageExt对象问题(1.2.78之前版本)

前言 无论是kafka&#xff0c;还是RocketMq&#xff0c;消费者方法参数中的MessageExt对象不能被 fastjson默认的方式序列化。 一、查看代码 Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,ConsumeConcurrentlyContext context) {t…

江科大stm32学习笔记——【4-1】OLED

一.原理 1.调试方式 串口调试&#xff1a;通过串口通信&#xff0c;将调试信息发送到电脑端&#xff0c;电脑使用串口助手显示调试信息。 显示屏调试&#xff1a;直接将显示屏连接到单片机&#xff0c;将调试信息打印在显示屏上。 Keil调试模式&#xff1a;借助Keil软件的调…

resilience4j 2.0.0版本使用要求最低JDK17(使用踩坑记录)

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

六自由度Stewart平台的matlab模拟与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1运动学原理 4.2 Stewart平台运动学方程 5.完整工程文件 1.课题概述 六自由度Stewart平台的matlab模拟与仿真&#xff0c;模拟六自由度Stewart平台的动态变化情况以及伺服角度。 2.系统仿真结果 3.核…

v-rep--websocket接口

websocket是什么 V-REP 中的 Web Socket 是一种用于在 V-REP 和外部应用程序之间进行通信的协议和技术。Web Socket 基于 TCP 连接&#xff0c;可以提供双向、实时的数据传输&#xff0c;适用于互动性或实时交互性应用。 (比如v-rep在云服务器上运行&#xff0c;通过websocke…

【国产MCU】-CH32V307-定时器同步模式

定时器同步模式 文章目录 定时器同步模式1、定时器同步模式介绍2、驱动API介绍3、定时器同步模式实例1、定时器同步模式介绍 CH32V307的定时器能够输出时钟脉冲(TRGO),也能接收其他定时器的输入(ITRx)。不同的定时器的ITRx的来源(别的定时器的TRGO)是不一样的。 通用定…

RecyclerView适配器的封装

RecyclerView适配器基类的封装 RecyclerView.Adapter封装类 public abstract class AutoRollAdapter<T> extends RecyclerView.Adapter<ViewHolder> {protected List<T> ts new ArrayList<>();public List<T> getData() {return ts; } public…

Covalent Network的长期数据可用性 获得了众多加密 KOL的肯定及支持

随着 Web3 生态系统的动态发展&#xff0c;Covalent Network&#xff08;CQT&#xff09;的关键性正在显现&#xff0c;通过提供分布式、加密安全的数据层&#xff0c;以解决长期数据可用性的问题。Covalent Network&#xff08;CQT&#xff09;不仅仅是一个工具&#xff0c;更…

企业内部文件资料如何进行加密 ——防止泄露?

企业内部文件资料的加密是防止数据泄露的关键措施之一。 www.weaem.com 以下是一些建议&#xff0c;用于在企业内部进行文件资料的加密&#xff0c;以防止数据泄露&#xff1a; 选择适合的加密技术&#xff1a; 透明加密&#xff1a;这种加密方式允许用户在不改变原有操作习惯的…

简单网站模板1(HTML)

想要拥有自己的网站&#xff0c;却不知该如何才能简约好看&#xff0c;接下来分享一种自己搭建的网站模板&#xff0c;希望大家喜欢。 展示图&#xff1a; CODE: <!DOCTYPE html> <html> <head><title>我的网站</title><style>body {fo…

BERT:基于TensorFlow的BERT模型搭建中文问答系统模型

目录 1、导入所需库2、准备数据集3、对问题和答案进行分词4、构建模型5、编译模型6、训练模型7、评估模型8、使用模型进行预测 1、导入所需库 #导入numpy库&#xff0c;用于进行数值计算 import numpy as np#从Keras库中导入Tokenizer类&#xff0c;用于将文本转换为序列 from…

Nginx服务器配置详解

server {listen 80;listen 443 ssl http2;server_name logi.lyz8770.com;index index.html index.htm default.htm default.html;root /www/wwwroot/logi.lyz8770.com/dist/;#SSL-START SSL相关配置#error_page 404/404.html;ssl_certificate /www/server/panel/vhost/cert/…

eltable 合计行添加tooltip

eltable 合计行添加tooltip 问题描述&#xff1a; eltable 合计行单元格内容过长会换行&#xff0c;需求要求合计行数据超长显示 … &#xff0c;鼠标 hover 时显示提示信息。 解决方案&#xff1a;eltable合计行没有对外的修改接口&#xff0c;想法是 自己实现一个tooltip&a…

System Verilog学习笔记(八)——采样和数据驱动

System Verilog学习笔记&#xff08;八&#xff09;——采样和数据驱动 为了避免在RTL仿真行为中发生的信号竞争问题&#xff0c;可以通过非阻塞赋值或者特定信号延迟来解决同步的问题。 默认情况下&#xff0c;时钟对于组合电路的驱动会添加一个无限小时间&#xff08;delta-…

RLWE同态加密编码打包——系数打包

RLWE同态加密的明文域 RLWE的加密方案&#xff0c;如BGV、BFV&#xff0c;加密的对象&#xff0c;实际上是分圆多项式环上的一个整系数多项式。而我们在平时接触到的需要加密的数据&#xff0c;如图像或者工资&#xff0c;通常是一个数。所以&#xff0c;在使用RLWE同态加密时…

JVM对象创建与内存分配机制

JVM对象创建与内存分配机制 JVM对象创建与内存分配机制 JVM对象创建与内存分配机制对象的创建过程内存分配对象栈上分配对象逃逸分析标量替换 对象在Eden区分配大对象直接进入老年代长期存活的对象将进入老年代对象年龄动态判断老年代空间分配担保机制 对象头与指针压缩对象头利…

课时49:表达式_表达式进阶_集合基础

3.3.2 集合基础 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 所谓的集合&#xff0c;主要是针对多个条件表达式组合后的结果&#xff0c;尤其是针对于逻辑场景的组合。初中数学的相关逻辑示意图&#xff1a;表现样式 两个…

将四个主页面配置为 tab-bar 的子路由

使用vant组件库 路由 {path: /, name: layout,component: () > import(/views/layout),children: [{path: , // 默认子路由name: home,component: () > import(/views/home)},{path: qa,name: qa,component: () > import(/views/qa)},{path: video,name: video,compo…