八、桥接模式

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 OperatingSystem 抽象类
    • 2.2 LinuxOS 类
    • 2.3 WindowsOS 类
    • 2.4 FileOperation 类
    • 2.5 FileAppender 类
    • 2.6 FileReplicator 类
    • 2.7 Client 类
    • 2.8 Client 类运行结果
    • 2.9 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 Implementor ( 实现者 )
      • 3.1.2 ConcreteImplementor ( 具体实现者 )
      • 3.1.3 Abstraction ( 抽象化 )
      • 3.1.4 RefinedAbstraction ( 改善后的抽象化 )
    • 3.2 类图
  • 4 注意事项
  • 5 其思想在源码中的使用
  • 6 优缺点
  • 7 适用场景
  • 8 总结


1 基本介绍

桥接模式(Bridge Pattern)是一种 结构型 设计模式,它的主要目的是将 功能实现 分离,使它们都可以 独立地变化

2 案例

本案例的目的是:在 Linux 和 Windows 系统中,对文件使用不同的操作。

两个系统的文件系统都使用 Map 来实现,为了凸显出两个系统的不同,使用不同的 Map 实现类:

  • 在 Linux 系统中使用 ConcurrentHashMap(线程安全)作为 Map 的实现类
  • 在 Windows 系统中使用 HashMap(线程不安全)作为 Map 的实现类。

文件的操作有两种:

  • 基础操作:打开文件、将内容覆盖文件、关闭文件。
  • 进阶操作:需要 复合 基础操作的操作,共有以下三种:
    • 追加操作:将指定内容追加到文件尾部。
    • 复制操作:将指定源文件复制到指定目标文件。
    • 展示操作:展示文件的内容。注意:由于不想导致类的关系太过复杂,所以将其放到基础操作中实现。

2.1 OperatingSystem 抽象类

public abstract class OperatingSystem { // 操作系统 抽象类/*** 打开文件,如果没有,就新建一个文件* @param fileName 待打开文件名* @return 文件中的内容*/public abstract String openFile(String fileName);/*** 修改文件* @param fileName 待修改文件名* @param content 新的内容*/public abstract void modifyFile(String fileName, String content);/*** 关闭文件* @param fileName 待关闭文件名*/public abstract void closeFile(String fileName);
}

2.2 LinuxOS 类

public class LinuxOS extends OperatingSystem { // Linux 操作系统,它的文件系统线程安全/*** 文件系统,key 为 文件名,value 为 文件内容* 使用 ConcurrentHashMap 保证线程安全*/private final Map<String, String> fileSystem = new ConcurrentHashMap<>();@Overridepublic String openFile(String fileName) {if (!fileSystem.containsKey(fileName)) { // 如果不存在某个文件fileSystem.put(fileName, ""); // 则创建一个空文件}return fileSystem.get(fileName);}@Overridepublic void modifyFile(String fileName, String content) {fileSystem.put(fileName, content);}@Overridepublic void closeFile(String fileName) {// 在本案例中无需实现}
}

2.3 WindowsOS 类

public class WindowsOS extends OperatingSystem { // Windows 操作系统,它的文件系统线程不安全/*** 文件系统,key 为 文件名,value 为 文件内容* 使用 HashMap 可能会出现线程不安全的问题*/private final Map<String, String> fileSystem = new HashMap<>();@Overridepublic String openFile(String fileName) {if (!fileSystem.containsKey(fileName)) { // 如果不存在某个文件fileSystem.put(fileName, ""); // 则创建一个空文件}return fileSystem.get(fileName);}@Overridepublic void modifyFile(String fileName, String content) {fileSystem.put(fileName, content);}@Overridepublic void closeFile(String fileName) {// 在本案例中无需实现}
}

2.4 FileOperation 类

public class FileOperation { // 基础文件操作的类private final OperatingSystem os; // 操作系统public FileOperation(OperatingSystem os) {this.os = os;}/*** 打开文件,如果没有,就新建一个文件* @param fileName 待打开文件名* @return 文件中的内容*/public String openFile(String fileName) {return os.openFile(fileName);}/*** 修改文件* @param fileName 待修改文件名* @param content 新的内容*/public void modifyFile(String fileName, String content) {os.modifyFile(fileName, content);}/*** 关闭文件* @param fileName 待关闭文件名*/public void closeFile(String fileName) {os.closeFile(fileName);}/*** 显示文件的内容* @param fileName 待显示内容的文件名*/public void displayFile(String fileName) {String content = os.openFile(fileName);System.out.println("-----------------------------------------------------------");System.out.println("[" + fileName + "]");System.out.println(content);System.out.println("-----------------------------------------------------------");os.closeFile(fileName);}
}

2.5 FileAppender 类

public class FileAppender extends FileOperation { // 处理 追加内容操作 的类public FileAppender(OperatingSystem os) {super(os);}/*** 将指定内容追加到文件尾部* @param fileName 待追加文件名* @param appendContent 追加内容*/public void append(String fileName, String appendContent) {String content = super.openFile(fileName); // 指定文件原有的内容content += appendContent; // 将新内容追加到原有的内容后super.modifyFile(fileName, content); // 修改文件super.closeFile(fileName); // 关闭文件}
}

2.6 FileReplicator 类

public class FileReplicator extends FileOperation { // 处理 复制文件操作 的类public FileReplicator(OperatingSystem os) {super(os);}/*** 将 源文件的内容 拷贝到 目标文件中* @param srcFileName 源文件名称* @param dstFileName 目标文件名称*/public void replicate(String srcFileName, String dstFileName) {String srcContent = super.openFile(srcFileName); // 打开源文件,并获取源文件的内容super.openFile(dstFileName); // 打开目标文件super.modifyFile(dstFileName, srcContent); // 将 目标文件的内容 修改为 源文件的内容super.closeFile(dstFileName); // 关闭目标文件super.closeFile(srcFileName); // 关闭源文件}
}

2.7 Client 类

public class Client { // 使用 两种系统 分别执行 两种操作 的客户端public static void main(String[] args) {// 文件名称final String srcFileName = "src.txt"; // 源文件final String dstFileName = "dst.txt"; // 目标文件// 操作系统OperatingSystem linuxOS = new LinuxOS();OperatingSystem windowsOS = new WindowsOS();// 各种文件操作FileOperation linuxFileOperation = new FileOperation(linuxOS);FileAppender linuxFileAppender = new FileAppender(linuxOS);FileReplicator linuxFileReplicator = new FileReplicator(linuxOS);FileOperation windowsFileOperation = new FileOperation(windowsOS);FileAppender windowsFileAppender = new FileAppender(windowsOS);FileReplicator windowsFileReplicator = new FileReplicator(windowsOS);// 先分别在 Linux 和 Windows 系统中创建一个文件,并写入不同内容,之后查看内容System.out.println("操作一:创建文件并写入内容");linuxFileOperation.openFile(srcFileName);linuxFileOperation.modifyFile(srcFileName, "Hello, LinuxOS!");linuxFileOperation.closeFile(srcFileName);linuxFileOperation.displayFile(srcFileName);windowsFileOperation.openFile(srcFileName);windowsFileOperation.modifyFile(srcFileName, "Hello, WindowsOS!");windowsFileOperation.closeFile(srcFileName);windowsFileOperation.displayFile(srcFileName);System.out.println("===============================" +"=============================="); // 分隔// 然后给这两个文件追加不同的内容,并查看System.out.println("操作二:给文件追加内容");linuxFileAppender.append(srcFileName,"\nI am a programmer using Linux system.");linuxFileOperation.displayFile(srcFileName);windowsFileAppender.append(srcFileName,"\nI am a programmer using Windows system.");windowsFileOperation.displayFile(srcFileName);System.out.println("===============================" +"=============================="); // 分隔// 最后将两个文件复制到新的文件中,并查看System.out.println("操作三:复制文件内容");linuxFileReplicator.replicate(srcFileName, dstFileName);linuxFileOperation.displayFile(dstFileName);windowsFileReplicator.replicate(srcFileName, dstFileName);windowsFileOperation.displayFile(dstFileName);}
}

2.8 Client 类运行结果

操作一:创建文件并写入内容
-----------------------------------------------------------
[src.txt]
Hello, LinuxOS!
-----------------------------------------------------------
-----------------------------------------------------------
[src.txt]
Hello, WindowsOS!
-----------------------------------------------------------
=============================================================
操作二:给文件追加内容
-----------------------------------------------------------
[src.txt]
Hello, LinuxOS!
I am a programmer using Linux system.
-----------------------------------------------------------
-----------------------------------------------------------
[src.txt]
Hello, WindowsOS!
I am a programmer using Windows system.
-----------------------------------------------------------
=============================================================
操作三:复制文件内容
-----------------------------------------------------------
[dst.txt]
Hello, LinuxOS!
I am a programmer using Linux system.
-----------------------------------------------------------
-----------------------------------------------------------
[dst.txt]
Hello, WindowsOS!
I am a programmer using Windows system.
-----------------------------------------------------------

2.9 总结

在本案例中,OperatingSystem 抽象类 和 LinuxOS, WindowsOS 类组成了 实现FileOperation, FileAppender, FileReplicator 类组成了 功能,使用 FileOperation 类将 实现功能 分隔开来。

由于不想再增加本案例的复杂度,所以将 展示操作 放到本类中,依照本模式的思想,应该将 展示操作 也放到一个单独的类中,大家可以将 写一个类来处理 展示操作 当作练习。

在添加新的功能(或实现)时,无需为其添加对应的实现(或功能)类

  • 如果想要添加一个新功能, 需要继承 功能 体系中的某个类即可。如将 展示操作 写到独立的类中。
  • 如果想要添加一个新实现, 需要继承 OperatingSystem 抽象类即可。如添加 MacOS 类。

3 各角色之间的关系

3.1 角色

3.1.1 Implementor ( 实现者 )

该角色负责 定义 用于实现 Abstraction 角色的接口的 方法,位于 实现 体系的顶端。在本案例中,OperatingSystem 抽象类扮演该角色。

3.1.2 ConcreteImplementor ( 具体实现者 )

该角色负责 实现 在 Implementor 角色中定义的 方法。在本案例中,LinuxOS, WindowsOS 类都扮演了该角色。

3.1.3 Abstraction ( 抽象化 )

该角色负责 使用 Implementor 角色的方法,定义基本的功能,位于 功能 体系的顶端。此外,还需要保存 Implementor 角色的实例。在本案例中,FileOperation 类扮演了该角色。

3.1.4 RefinedAbstraction ( 改善后的抽象化 )

该角色负责 在 Abstraction 角色的基础上添加新功能。在本案例中,FileAppender, FileReplicator 类都扮演了该角色。

3.2 类图

alt text
说明:

  • Implementor 角色中的方法 method1(), method2()abstract 的,强制其子类实现。
  • Implementor 角色也可以是接口,这样一来,ConcreteImplementor 角色与它之间的关系就是 实现 了。
  • Abstraction 角色 和 Implementor 角色 中间的 聚合 关系 就代表了 桥接模式 的 桥接

4 注意事项

  • 分离 功能 和 实现:在使用桥接模式进行设计时,需要 辨析 系统的 功能实现,从而将其分离开来。这无疑增加了设计难度。
  • 使用范围局限性:如果想要使用桥接模式,则系统中必须存在 两个或多个独立变化的维度,并且这些维度需要 独立扩展。如果系统中没有这样的应用场景,强行使用桥接模式可能会导致设计 过度复杂,反而降低系统的可维护性和可扩展性。

5 其思想在源码中的使用

JDBC(Java Database Connectivity)在使用桥接模式时,主要体现在它如何处理 数据库驱动的加载数据库的使用 上:

  • 实现体系
    • 抽象实现:在 JDBC 中,java.sql.Driver 接口扮演了 Implementor 角色。它定义了一个驱动应该实现的接口,包括如何连接到数据库等方法。
    • 具体实现:各个数据库厂商提供的驱动类(如 com.mysql.cj.jdbc.Driver)(直接或间接地) 实现了 java.sql.Driver 接口,扮演 ConcreteImplementor 角色。这些驱动类包含了与特定数据库交互的具体逻辑。
  • 功能体系——DriverManager 类
    • DriverManager 类在 JDBC 中起到了 桥梁 的作用,它维护了一个已注册的 JDBC 驱动列表(registeredDrivers)。当通过 Class.forName() 方法加载数据库驱动时,实际上是在调用 该驱动类的静态代码块,其中包含了 将自身实例注册到 DriverManagerregisteredDrivers 列表中 的代码。
    • 当调用 DriverManager.getConnection() 方法获取数据库连接时,DriverManager 会遍历 registeredDrivers 列表,尝试使用列表中的每个驱动与数据库建立连接,直到找到成功的连接或所有驱动都尝试失败为止。
    • 此外,DriverManager 类独立承担了所有的 功能,没有将更复杂的功能交给子类实现,这就是 JDBC 使用的桥接模式 与 常规桥接模式 的不同之处。
  • 实现 与 功能 的分离
    • 实现——数据库驱动:不同的数据库需要不同的驱动来实现与 JDBC 的交互。
    • 功能——数据库操作DriverManager 类提供了所有的数据库操作。
  • 桥接模式的优势
    • 扩展性强:由于功能与实现分离,当需要支持新的数据库时,只需提供新的驱动实现即可,无需修改现有的 JDBC 代码。
    • 耦合度低:JDBC 的抽象接口与具体数据库实现之间通过 DriverManager 进行桥接,降低了它们之间的耦合度。

6 优缺点

优点

  • 提高可扩展性:桥接模式将 功能实现 分离,使得它们可以 独立地变化。这意味着我们可以在不修改抽象代码的情况下,通过增加新的实现类来扩展系统的功能。
  • 符合开闭原则:桥接模式符合开闭原则,即对扩展开放,对修改关闭。这意味着我们可以在不修改现有代码的情况下,通过扩展系统来满足新的需求。
  • 降低系统的耦合度:通过桥接模式,我们可以减少类之间的直接依赖关系,降低系统的耦合度。这有助于提高系统的 可维护性可测试性
  • 支持多种变化:当一个类存在 多个角度的变化 时(例如,一个类既需要支持不同的操作,又需要支持不同的实现方式),桥接模式可以很好地处理这种情况,因为它允许在多个维度上进行变化。

缺点

  • 增加系统的复杂性:桥接模式增加了系统的抽象层次,使得系统的理解和实现变得更加复杂。特别是当系统中存在大量的抽象类和实现类时,这种复杂性可能会变得难以管理。
  • 增加设计的难度:在设计初期,正确地识别出哪些部分(功能部分)应该 被抽象化,哪些部分(实现部分)应该 被实现化并不是一件容易的事情。这需要设计者对系统有深入的理解和丰富的经验。
  • 增加对象的数量:由于桥接模式将 功能 和 实现 分离,因此在运行时可能会产生大量的对象。这可能会增加系统的内存开销,并降低系统的性能。
  • 过度设计:在某些情况下,可能会存在过度使用桥接模式的风险。如果系统中的变化维度并不明显,或者变化的可能性很小,那么使用桥接模式可能会导致过度设计,增加不必要的复杂性。

7 适用场景

  • 数据库连接 与 驱动:在连接数据库时,桥接模式可以用于分离 数据库连接接口(功能)和 不同的数据库驱动程序(实现)。这样,在不修改连接代码的情况下,可以轻松地切换不同的数据库。例如,在 JDBC(Java Database Connectivity)中,Driver 接口作为抽象部分,不同的数据库厂商(如 MySQL、Oracle)提供的驱动实现类作为实现部分,通过桥接模式实现数据库的灵活切换。
  • 日志记录 与 输出目标:在记录日志时,桥接模式可以将 日志记录器(功能)与 不同的日志输出目标(如控制台、文件、数据库等,实现)解耦。这样,可以在不修改日志记录代码的情况下,轻松地切换日志输出目标。例如,在日志框架中,可以定义一个日志记录器接口,不同的日志实现类(如控制台日志实现类、文件日志实现类等)分别实现该接口,通过桥接模式实现日志记录的灵活配置。
  • 消息队列 与 协议:在使用消息队列时,桥接模式可以用于分离 消息队列客户端接口(功能)和 不同的消息队列协议(如 RocketMQ、Kafka等,实现)。这样,可以在不修改消息队列代码的情况下,轻松地切换不同的消息队列协议。例如,在消息队列客户端框架中,可以定义一个消息队列客户端接口,不同的消息队列协议实现类分别实现该接口,通过桥接模式实现消息队列的灵活选择。
  • 图形用户界面(GUI):在图形用户界面(GUI)框架中,桥接模式可以用于分离 GUI 组件的抽象表示具体渲染实现。例如,在 Swing 或 JavaFX 等 GUI 框架中,可以将组件的抽象行为(如按钮点击事件)与具体的渲染逻辑(如按钮的外观和样式)分离,通过桥接模式实现 GUI 组件的灵活定制和扩展。
  • 插件系统:在插件系统中,桥接模式可以用于分离 插件的接口定义具体的插件实现。这样,可以在不修改主程序代码的情况下,通过添加新的插件实现来扩展程序的功能。插件系统广泛应用于各种软件开发中,如 IDE(集成开发环境)、游戏平台等,桥接模式为这些系统提供了良好的扩展性和灵活性。

8 总结

桥接模式 是一种 结构型 设计模式,它的主要目的是将 功能实现 分离,使它们都可以 独立地变化。这种分离方式增强了系统的 灵活性可扩展性,允许在不修改抽象代码的情况下增加新的实现。桥接模式通过 聚合关系 而非 继承关系 来实现这一点,从而减少了类之间的耦合。

此外,在使用本模式之前一定要理清系统的逻辑,认清哪部分是 功能,哪部分是 实现。如果功能和实现变化的程度不大,则 不要为了使用桥接模式而使用桥接模式,这时简单一点,将其耦合起来比较方便。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

ARTMO Table ‘db1.test_mla_result‘ doesn‘t exist解决方案

com.mysql.jdbc.JDBC4PreparedStatement3f3c966c: describe test_mla_result; Java exception occurred: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table db1.test_mla_result doesnt exist解决方案&#xff1a; 打开MySQL的command Line, 输入SHOW TABLES…

基于微信小程序的高校排课系统 /基于微信小程序的排课管理系统/课程管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

Vue2和Vue3实战代码中的小差异(实时更新)

目录 前言1. 未使用自闭合标签2. 事件名连字符3. 换行符4. 弃用.sync 前言 以下文章实时更新&#xff0c;主打记录差异 1. 未使用自闭合标签 104:7 error Require self-closing on Vue.js custom components (<el-table-column>) vue/html-self-closing✖ 1 problem…

Canal监听Mysql回写Redis

目录 一、canal服务端 1.1 下载 1.2 解压 1.3 配置 1.4 启动 1.5 查看 二、canal客户端&#xff08;Java编写业务程序&#xff09; 2.1 SQL脚本 2.2 写POM 2.3 写Yaml 2.4 写业务类 2.4.1.项目结构 2.4.2 Utils.RedisUtil 2.4.3 biz.RedisCanalClientExample 一、…

麦田物语第十五天

系列文章目录 麦田物语第十五天 文章目录 系列文章目录一、构建游戏的时间系统二、时间系统 UI 制作总结 一、构建游戏的时间系统 在该游戏中我们要构建年月日天时分秒等时间的概念&#xff0c;从而实现季节的更替&#xff0c;昼夜的更替等&#xff08;不同的季节可以播种不同…

基于gaussian计算NICS值评估分子体系的芳香性和反芳香性

计算分子NICS值的基本流程 其中第1行的Bq即为NICS(0)虚原子对应的位置&#xff0c;7.5538为NICS(0)对应虚原子位置处的各向同性化学位移屏蔽值。 由于NICS值为各向同性化学屏蔽值的负值&#xff0c;因此苯的NICS(0)和NICS(1)分别为-7.5538和-10.5301&#xff0c;这也表明苯分…

通信类IEEE会议——第四届通信技术与信息科技国际学术会议(ICCTIT 2024)

[IEEE 独立出版&#xff0c;中山大学主办&#xff0c;往届均已见刊检索] 第四届通信技术与信息科技国际学术会议&#xff08;ICCTIT 2024&#xff09; 2024 4th International Conference on Communication Technology and Information Technology 重要信息 大会官网&#xf…

python生成系统测试数据

开发系统的时候,为了系统可以更好的进行测试,一般需要准备测试数据,以便可以顺利的对各种场景进行测试,使用两张表来说明怎么快速生成测试数据。 1.用户表 一般登录的时候,需要用到用户表 用户表字段如下: 用户名、密码、姓名、性别、邮箱、手机号、用户类型、地址 下…

Linux进程间通信(管道+共享内存)

进程间通信&#xff08;interprocess communication&#xff0c;简称 IPC&#xff09;指两个进程之间的通信。系统中的每一个进程都有各自的地址空间&#xff0c;并且相互独立、隔离&#xff0c;每个进程都处于自己的地址空间中。所以同一个进程的不同模块&#xff08;譬如不同…

【PyQt5】一文向您详细介绍 setPlaceholderText() 的作用

【PyQt5】一文向您详细介绍 setPlaceholderText() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通…

剑和沙盒 6 - 线程辱骂 – 使用线程名称进行攻击

强调&#xff1a; 进程注入是攻击者工具包中的重要技术之一。在下面的文章中 解释了如何滥用线程描述 API 来绕过端点保护产品。提出了一种新的注入技术&#xff1a;Thread Name-Calling&#xff0c;并给出了实施保护的相关建议。 介绍 进程注入是攻击者使用的重要技术之一 。…

《Programming from the Ground Up》阅读笔记:p75-p87

《Programming from the Ground Up》学习第4天&#xff0c;p75-p87总结&#xff0c;总计13页。 一、技术总结 1.persistent data p75, Data which is stored in files is called persistent data, because it persists in files that remain on disk even when the program …

一文理解生成式AI应用的五个级别:Tool、Chatbot、Copilot、Agent 和 Intelligence

当下&#xff0c;很多人对 AI 一知半解&#xff0c;并不能很好地区分&#xff1a;Tool、Chatbot、Copilot、Agent 和 Intelligence 概念之间的区别。 最近读完 《真格基金戴雨森谈生成式AI&#xff1a;这是比移动互联网更大的创业机会&#xff0c;开始行动是关键 》 发现讲的特…

机器人无人机视觉避障常见方式及优缺点总结

视觉避障是一种通过视觉传感器&#xff08;如摄像头&#xff09;捕捉环境图像信息&#xff0c;经过图像处理和计算机视觉算法分析&#xff0c;识别并定位障碍物&#xff0c;进而实现避障的技术。 一、常见方式 机器人无人机视觉避障是指通过视觉传感器获取周围环境信息&#x…

html+css 实现悬浮按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

AI机器人本地免费部署(部署Llama 3.1详细教程)

昨日&#xff0c;Meta公司发布了人工智能模型——Llama 3.1。 那么Llama 3.1 405B的效果怎么样&#xff1f;我们来对比一张图&#xff0c;横向对比一下GPT-4。 可以看出&#xff0c;Llama 3.1 405B在各类任务中的表现可以与GPT-4等顶级的模型相差无几。 那么&#xff0c;我们…

2024年国际高校数学建模大赛(IMMCHE)问题A:金字塔石的运输完整思路代码 结果分享(仅供学习)

2024 International Mathematics Molding Contest for Higher Education Problem A: Transportation of Pyramid Stones&#xff08;2024年国际高校数学建模大赛&#xff08;IMMCHE&#xff09;问题A&#xff1a;金字塔石的运输&#xff09; 金字塔的建造是古埃及文明的杰出成…

嵌入式Python、决策树算法、SQLite、Flask、树莓派、机器学习:基于算法自主决策的智能家居系统(代码示例)

项目概述 随着物联网技术的快速发展&#xff0c;智能家居系统越来越普及&#xff0c;成为现代家庭生活的重要组成部分。本文将介绍一个基于Raspberry Pi和Arduino的智能家居算法控制系统的硬件平台。该系统能够通过传感器采集环境数据&#xff0c;并利用机器学习算法进行分析与…

Unity 资源 之 Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享

精彩呈现&#xff1a;Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享 一、Pop It 3D 解压玩具的魅力二、双人游戏的互动乐趣三、Unity 游戏资源包的优势四、如何获取资源包 亲爱的游戏爱好者们&#xff0c;今天为大家带来一款令人兴奋的游戏资源——Pop It 3D 解压玩具双人带…

VMware虚拟机中CentOS7自定义ip地址并且固定ip

配置固定ip(虚拟机) 前提&#xff1a;虚拟机网络配置成&#xff0c;自定义网络并选择VMnet8(NAT 模式) 操作(如下图)&#xff1a;点击虚拟机–》设置–》–》硬件–》网络适配器–》自定义&#xff1a;特定虚拟网络–》选择&#xff1a;VMnet8(NAT 模式) 虚拟机网络设置 需要记…