2. 结构型模式 - 桥接模式
亦称: Bridge
意图
桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用
问题
抽象? 实现? 听上去挺吓人? 让我们慢慢来, 先考虑一个简单的例子。
假如你有一个几何 形状
Shape类, 从它能扩展出两个子类: 圆形
Circle和 方形
Square 。 你希望对这样的类层次结构进行扩展以使其包含颜色, 所以你打算创建名为 红色
Red和 蓝色
Blue的形状子类。 但是, 由于你已有两个子类, 所以总共需要创建四个类才能覆盖所有组合, 例如 蓝色圆形
BlueCircle和 红色方形
RedSquare 。
所有组合类的数量将以几何级数增长。
在层次结构中新增形状和颜色将导致代码复杂程度指数增长。 例如添加三角形状, 你需要新增两个子类, 也就是每种颜色一个; 此后新增一种新颜色需要新增三个子类, 即每种形状一个。 如此以往, 情况会越来越糟糕。
解决方案
问题的根本原因是我们试图在两个独立的维度——形状与颜色——上扩展形状类。 这在处理类继承时是很常见的问题。
桥接模式通过将继承改为组合的方式来解决这个问题。 具体来说, 就是抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。
将一个类层次转化为多个相关的类层次, 避免单个类层次的失控。
根据该方法, 我们可以将颜色相关的代码抽取到拥有 红色
和 蓝色
两个子类的颜色类中, 然后在 形状
类中添加一个指向某一颜色对象的引用成员变量。 现在, 形状类可以将所有与颜色相关的工作委派给连入的颜色对象。 这样的引用就成为了 形状
和 颜色
之间的桥梁。 此后, 新增颜色将不再需要修改形状的类层次, 反之亦然。
抽象部分和实现部分
设计模式四人组的著作 在桥接定义中提出了抽象部分和实现部分两个术语。 我觉得这些术语过于学术了, 反而让模式看上去比实际情况更加复杂。 在介绍过形状和颜色的简单例子后, 我们来看看四人组著作中让人望而生畏的词语的含义。
抽象部分 (也被称为接口) 是一些实体的高阶控制层。 该层自身不完成任何具体的工作, 它需要将工作委派给实现部分层 (也被称为平台)。
注意, 这里提到的内容与编程语言中的接口或抽象类无关。 它们并不是一回事。
在实际的程序中, 抽象部分是图形用户界面 (GUI), 而实现部分则是底层操作系统代码 (API), GUI 层调用 API 层来对用户的各种操作做出响应。
一般来说, 你可以在两个独立方向上扩展这种应用:
- 开发多个不同的 GUI (例如面向普通用户和管理员进行分别配置)
- 支持多个不同的 API (例如, 能够在 Windows、 Linux 和 macOS 上运行该程序)。
在最糟糕的情况下, 程序可能会是一团乱麻, 其中包含数百种条件语句, 连接着代码各处不同种类的 GUI 和各种 API。
在庞杂的代码中, 即使是很小的改动都非常难以完成, 因为你必须要在整体上对代码有充分的理解。 而在较小且定义明确的模块中, 进行修改则要容易得多。
你可以将特定接口-平台的组合代码抽取到独立的类中, 以在混乱中建立一些秩序。 但是, 你很快会发现这种类的数量很多。 类层次将以指数形式增长, 因为每次添加一个新的 GUI 或支持一种新的 API 都需要创建更多的类。
让我们试着用桥接模式来解决这个问题。 该模式建议将类拆分为两个类层次结构:
- 抽象部分: 程序的 GUI 层。
- 实现部分: 操作系统的 API。
创建跨平台应用程序的一种方法
抽象对象控制程序的外观, 并将真实工作委派给连入的实现对象。 不同的实现只要遵循相同的接口就可以互换, 使同一 GUI 可在 Windows 和 Linux 下运行。
最后的结果是: 你无需改动与 API 相关的类就可以修改 GUI 类。 此外如果想支持一个新的操作系统, 只需在实现部分层次中创建一个子类即可。
桥接模式结构
-
抽象部分 (Abstraction) 提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。
-
实现部分 (Implementation) 为所有具体实现声明通用接口。 抽象部分仅能通过在这里声明的方法与实现对象交互。
抽象部分可以列出和实现部分一样的方法, 但是抽象部分通常声明一些复杂行为, 这些行为依赖于多种由实现部分声明的原语操作。
-
具体实现 (Concrete Implementations) 中包括特定于平台的代码。
-
精确抽象 (Refined Abstraction) 提供控制逻辑的变体。 与其父类一样, 它们通过通用实现接口与不同的实现进行交互。
-
通常情况下, 客户端 (Client) 仅关心如何与抽象部分合作。 但是, 客户端需要将抽象对象与一个实现对象连接起来。
伪代码
示例演示了桥接模式如何拆分程序中同时管理设备及其遥控器的庞杂代码。 设备
Device类作为实现部分, 而 遥控器
Remote类则作为抽象部分。
最初类层次结构被拆分为两个部分: 设备和遥控器。
遥控器基类声明了一个指向设备对象的引用成员变量。 所有遥控器通过通用设备接口与设备进行交互, 使得同一个遥控器可以支持不同类型的设备。
你可以开发独立于设备类的遥控器类, 只需新建一个遥控器子类即可。 例如, 基础遥控器可能只有两个按钮, 但你可在其基础上扩展新功能, 比如额外的一节电池或一块触摸屏。
客户端代码通过遥控器构造函数将特定种类的遥控器与设备对象连接起来。
// “抽象部分”定义了两个类层次结构中“控制”部分的接口。它管理着一个指向“实
// 现部分”层次结构中对象的引用,并会将所有真实工作委派给该对象。
class RemoteControl isprotected field device: Deviceconstructor RemoteControl(device: Device) isthis.device = devicemethod togglePower() isif (device.isEnabled()) thendevice.disable()elsedevice.enable()method volumeDown() isdevice.setVolume(device.getVolume() - 10)method volumeUp() isdevice.setVolume(device.getVolume() + 10)method channelDown() isdevice.setChannel(device.getChannel() - 1)method channelUp() isdevice.setChannel(device.getChannel() + 1)// 你可以独立于设备类的方式从抽象层中扩展类。
class AdvancedRemoteControl extends RemoteControl ismethod mute() isdevice.setVolume(0)// “实现部分”接口声明了在所有具体实现类中通用的方法。它不需要与抽象接口相
// 匹配。实际上,这两个接口可以完全不一样。通常实现接口只提供原语操作,而
// 抽象接口则会基于这些操作定义较高层次的操作。
interface Device ismethod isEnabled()method enable()method disable()method getVolume()method setVolume(percent)method getChannel()method setChannel(channel)// 所有设备都遵循相同的接口。
class Tv implements Device is// ……class Radio implements Device is// ……// 客户端代码中的某个位置。
tv = new Tv()
remote = new RemoteControl(tv)
remote.togglePower()radio = new Radio()
remote = new AdvancedRemoteControl(radio)
桥接模式适合应用场景
如果你想要拆分或重组一个具有多重功能的庞杂类 (例如能与多个数据库服务器进行交互的类), 可以使用桥接模式。
类的代码行数越多, 弄清其运作方式就越困难, 对其进行修改所花费的时间就越长。 一个功能上的变化可能需要在整个类范围内进行修改, 而且常常会产生错误, 甚至还会有一些严重的副作用。
桥接模式可以将庞杂类拆分为几个类层次结构。 此后, 你可以修改任意一个类层次结构而不会影响到其他类层次结构。 这种方法可以简化代码的维护工作, 并将修改已有代码的风险降到最低。
如果你希望在几个独立维度上扩展一个类, 可使用该模式。
桥接建议将每个维度抽取为独立的类层次。 初始类将相关工作委派给属于对应类层次的对象, 无需自己完成所有工作。
如果你需要在运行时切换不同实现方法, 可使用桥接模式。
当然并不是说一定要实现这一点, 桥接模式可替换抽象部分中的实现对象, 具体操作就和给成员变量赋新值一样简单。
顺便提一句, 最后一点是很多人混淆桥接模式和策略模式的主要原因。 记住, 设计模式并不仅是一种对类进行组织的方式, 它还能用于沟通意图和解决问题。
实现方式
-
明确类中独立的维度。 独立的概念可能是: 抽象/平台, 域/基础设施, 前端/后端或接口/实现。
-
了解客户端的业务需求, 并在抽象基类中定义它们。
-
确定在所有平台上都可执行的业务。 并在通用实现接口中声明抽象部分所需的业务。
-
为你域内的所有平台创建实现类, 但需确保它们遵循实现部分的接口。
-
在抽象类中添加指向实现类型的引用成员变量。 抽象部分会将大部分工作委派给该成员变量所指向的实现对象。
-
如果你的高层逻辑有多个变体, 则可通过扩展抽象基类为每个变体创建一个精确抽象。
-
客户端代码必须将实现对象传递给抽象部分的构造函数才能使其能够相互关联。 此后, 客户端只需与抽象对象进行交互, 无需和实现对象打交道。
桥接模式优缺点
- 你可以创建与平台无关的类和程序。
- 客户端代码仅与高层抽象部分进行互动, 不会接触到平台的详细信息。
- 开闭原则。 你可以新增抽象部分和实现部分, 且它们之间不会相互影响。
- 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
- 对高内聚的类使用该模式可能会让代码更加复杂。
与其他模式的关系
- 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。
- 桥接、 状态模式和策略模式 (在某种程度上包括适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。
- 你可以将抽象工厂模式和桥接搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
- 你可以结合使用生成器模式和桥接模式: 主管类负责抽象工作, 各种不同的生成器负责实现工作。
代码示例
/*** The Implementation defines the interface for all implementation classes. It* doesn't have to match the Abstraction's interface. In fact, the two* interfaces can be entirely different. Typically the Implementation interface* provides only primitive operations, while the Abstraction defines higher-* level operations based on those primitives.*/class Implementation {public:virtual ~Implementation() {}virtual std::string OperationImplementation() const = 0;
};/*** Each Concrete Implementation corresponds to a specific platform and* implements the Implementation interface using that platform's API.*/
class ConcreteImplementationA : public Implementation {public:std::string OperationImplementation() const override {return "ConcreteImplementationA: Here's the result on the platform A.\n";}
};
class ConcreteImplementationB : public Implementation {public:std::string OperationImplementation() const override {return "ConcreteImplementationB: Here's the result on the platform B.\n";}
};/*** The Abstraction defines the interface for the "control" part of the two class* hierarchies. It maintains a reference to an object of the Implementation* hierarchy and delegates all of the real work to this object.*/class Abstraction {/*** @var Implementation*/protected:Implementation* implementation_;public:Abstraction(Implementation* implementation) : implementation_(implementation) {}virtual ~Abstraction() {}virtual std::string Operation() const {return "Abstraction: Base operation with:\n" +this->implementation_->OperationImplementation();}
};
/*** You can extend the Abstraction without changing the Implementation classes.*/
class ExtendedAbstraction : public Abstraction {public:ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) {}std::string Operation() const override {return "ExtendedAbstraction: Extended operation with:\n" +this->implementation_->OperationImplementation();}
};/*** Except for the initialization phase, where an Abstraction object gets linked* with a specific Implementation object, the client code should only depend on* the Abstraction class. This way the client code can support any abstraction-* implementation combination.*/
void ClientCode(const Abstraction& abstraction) {// ...std::cout << abstraction.Operation();// ...
}
/*** The client code should be able to work with any pre-configured abstraction-* implementation combination.*/int main() {Implementation* implementation = new ConcreteImplementationA;Abstraction* abstraction = new Abstraction(implementation);ClientCode(*abstraction);std::cout << std::endl;delete implementation;delete abstraction;implementation = new ConcreteImplementationB;abstraction = new ExtendedAbstraction(implementation);ClientCode(*abstraction);delete implementation;delete abstraction;return 0;
}
执行结果
Abstraction: Base operation with: ConcreteImplementationA: Here's the result on the platform A.ExtendedAbstraction: Extended operation with: ConcreteImplementationB: Here's the result on the platform B.
相关文章:
2. 结构型模式 - 桥接模式
亦称: Bridge 意图 桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用 问题 抽象? 实现? 听上去挺吓人? 让我们慢慢来&#x…...
最小二乘法简介
最小二乘法简介 1、背景描述2、最小二乘法2.1、最小二乘准则2.2、最小二乘法 3、最小二乘法与线性回归3.1、最小二乘法与线性回归3.2、最小二乘法与最大似然估计 4、正态分布(高斯分布) 1、背景描述 在工程应用中,我们通常会用一组观测数据去…...
mathtype公式章节编号
1. word每章标题后插入章节符 如果插入后显示章节符,需要进行隐藏 开始->样式->MTEquationSection->修改样式->字体,勾选隐藏 2. 设置mathtype公式编号格式 插入编号->格式化->设置格式...
医学实验室检验科LIS信息系统源码
实验室信息管理是专为医院检验科设计的一套实验室信息管理系统,能将实验仪器与计算机组成网络,使病人样品登录、实验数据存取、报告审核、打印分发,实验数据统计分析等繁杂的操作过程实现了智能化、自动化和规范化管理。 实验室管理系统功能介…...
无需改动现有网络,企业高速远程访问内网Linux服务器
某企业为数据治理工具盒厂商,帮助客户摆脱数据问题困扰、轻松使用数据,使得客户可以把更多精力投入至数据应用及业务赋能,让数据充分发挥其作为生产要素的作用。 目前,该企业在北京、南京、西安、武汉等地均设有产研中心ÿ…...
Opencv入门五 (显示图片灰度值)
源码如下: #include <opencv2/opencv.hpp> int main(int argc, char** argv) { cv::Mat img_rgb, img_gry, img_cny; cv::namedWindow("Example Gray",cv::WINDOW_AUTOSIZE); cv::namedWindow("Example Canny", cv::WINDOW_…...
STM32F4 HAL流水灯Proteus仿真
源码下载:https://download.csdn.net/download/zlkk00/88654405...
【K8s】4# 使用kuboard部署开源项目实战
文章目录 1.开源项目2.实战2.1.创建spring-blade命名空间2.2.导入 spring-blade 到 K8S 名称空间2.3.设置存储卷参数2.4.调整节点端口2.5.确认导入2.6.查看集群2.7.导入配置到 nacos2.8.启动微服务工作负载 3.验证部署结果3.1.Nacos3.2. web 4.问题汇总Q1:Nacos启动…...
Mysql数据库(1)
目录 一.操作系统 二.数据库mysql 对象: 库 -> 表 -> 二维表格形式的结构化数据 常用的数据类型: MySQL的六大约束属性: SQL语句 :DDL DML DQL DCL 三.mysql的基础操作 查看当前服务器中的数据库 查看当前服务器中…...
网络安全-API接口安全
本文为作者学习文章,按作者习惯写成,如有错误或需要追加内容请留言(不喜勿喷) 本文为追加文章,后期慢慢追加 API接口概念 API接口(Application Programming Interface,应用程序编程接口&…...
flutter学习-day16-自定义组件
📚 目录 介绍组合多个组件自绘组件 Custompaint绘制边界RepaintBoundaryCustomPainter与Canvas画笔Paint绘制组件例子 本文学习和引用自《Flutter实战第二版》:作者:杜文 1. 介绍 当Flutter提供的现有组件无法满足我们的需求,或…...
XML简介 (EXtensible Markup Language)
XML简介 (EXtensible Markup Language) 可扩展标记语言 特点 XML与操作系统、编程语言的开发平台无关实现不同系统之间的数据交换 作用 数据交互配置应用程序和网站Ajax基石 XML标签 XML文档内容由一系列标签元素组成 <元素名 属性名"属性值">元素内容&l…...
基于Spring自动注入快速实现策略模式+工厂模式优化过多的if..else
一、策略模式 1.1策略模式定义 在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相…...
安装vcpkg管理opencv的安装+MFC缺失的解决
第一步,出现#include没有办法找到opencv头文件的问题,无法解决 在VC的提示下,安装了vcpkg,然后用vcpkg命令来帮助安装opencv,过程十分顺利。 1. cmd 到命令行窗口; 2. 建立src文件夹,并进入…...
了解树和学习二叉树
1.树 1.1 概念 树是一种 非线性 的数据结构,它是由 n ( n>0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看 起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。 注意:树形结构中…...
Spring Boot学习随笔- 拦截器实现和配置(HandlerInterceptor、addInterceptors)、jar包部署和war包部署
学习视频:【编程不良人】2021年SpringBoot最新最全教程 第十三章、拦截器 拦截器 :Interceptor 拦截 中断 类似于javaweb中的Filter,不过没有Filter那么强大 作用 Spring MVC的拦截器是一种用于在请求处理过程中进行预处理和后处理的机制。拦…...
Pipelined-ADC设计二——结构指标及非理想因素(Part2)
接上文,本章将两个比较重要的非理想因素,因此各项指标制定。后续会对常见的非理想因素给出常见的解决方法,以及设计所采用的方法。 2.2.7. 比较器失调 在流水线 ADC 中,比较器的主要误差来源就是比较器失调,称为失调误…...
Ubuntu 常用命令之 clear 命令用法介绍
📑Linux/Ubuntu 常用命令归类整理 clear命令在Ubuntu系统下用于清除终端屏幕的内容。这个命令没有任何参数,它的主要作用就是清理终端屏幕上的所有信息,使得屏幕看起来像是新打开的一样。 使用clear命令非常简单,只需要在终端中…...
【JAVA面试题】什么是对象锁?什么是类锁?
🍎 个人博客 :个 人 主 页 🏆个人专栏:多线程JAVA ⛳️ 功 不 唐 捐 ,玉 汝 于 成 目录 前言 回答 对象锁(Object Lock): 类锁(Class Lock)࿱…...
飞天使-k8s知识点5-kubernetes基础名词扫盲
文章目录 deploymentspodNodeserviceskubectl 实现应用伸缩kubectl 实现滚动更新kubernetes架构 deployments 中文文档 http://docs.kubernetes.org.cn/251.htmldeployment是用来创建和更新应用的,master 会负责将创建好的应用实例调度到集群中的各个节点 应用实例…...
【视觉实践】使用Mediapipe进行目标检测:杯子检测和椅子检测实践
目录 1 Mediapipe 2 Solutions 3 安装mediapipe 4 实践 1 Mediapipe Mediapipe是google的一个开源项目,可以提供开源的、跨平台的常用机器学习(machine learning,ML)方案。MediaPipe是一个用于构建机器学习管道</...
C++之深拷贝进阶
目录 拷贝构造函数的深拷贝进阶版本 赋值运算符重载的深拷贝进阶 总结 上期我们学习了C中深拷贝的传统版本,今天我们将学习更为高效的版本。 拷贝构造函数的深拷贝进阶版本 传统版本代码如下: string(string& s):_str(new char[strlen(s._str)…...
导行电磁波从纵向场分量求其他方向分量的矩阵表示
导行电磁波从纵向场分量求解其他方向分量的矩阵表示 导行电磁波传播的特点 电磁波在均匀、线性、各向同性的空间中沿着 z z z轴传播,可用分离变量法将时间轴、 z z z轴与 x , y x,y x,y轴分离,电磁波的形式可表示为: E ⃗ E ⃗ ( x , y )…...
融资项目——swagger2的注解
1. ApiModel与ApiModelProperty(在实体类中使用) 如上图,ApiModel加在实体类上方,用于整体描述实体类。ApiModelProperty(value"xxx",example"xxx")放于每个属性上方,用于对属性进行描述。swagger2网页上的效果如下图&am…...
【性能优化】MySql数据库查询优化方案
阅读本文你的收获 了解系统运行效率提升的整体解决思路和方向学会MySQl中进行数据库查询优化的步骤学会看慢查询、执行计划、进行性能分析、调优 一、问题:如果你的系统运行很慢,你有什么解决方案? 关于这个问题,我们通常首先…...
Chrome浏览器http自动跳https问题
现象: Chrome浏览器访问http页面时有时会自动跳转https,导致一些问题。比如: 开发阶段访问dev环境网址跳https,后端还是http,导致接口跨域。 复现: 先访问http网址,再改成https访问…...
【C++进阶02】多态
一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为,不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如:函数重载 动态多态指的是运行时 在程序运行…...
PHP开发日志——循环和条件语句嵌套不同,效率不同(循环内加入条件语句,条件语句判断后加入循环,array_map函数中加入条件语句)
十多年前开发框架时,为了效率不断试过各种代码写法,今天又遇到了,想想php8时代会不会有所变化,结果其实也还是和当年一样,但当年没写博客,但现在可以把数据记录下来了。 PHP_loop_ireflies_dark_forest 项目…...
【Seata源码学习 】 扫描@GlobalTransaction注解 篇一
1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制,在应用上下文启动时,会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…...
DBA-MySql面试问题及答案-上
文章目录 1.什么是数据库?2.如何查看某个操作的语法?3.MySql的存储引擎有哪些?4.常用的2种存储引擎?6.可以针对表设置引擎吗?如何设置?6.选择合适的存储引擎?7.选择合适的数据类型8.char & varchar9.Mysql字符集10.如何选择…...
宜昌城市建设学校网站/聊城网站seo
让云触手可及:微软虚拟化远程直播 2011年IT届的明星,非虚拟化莫属,特别是刚刚结束的微软2011TechED大会上,虚拟化更是受到了热捧,越来越多的人追随虚拟化的脚步,虚拟化如此火热,如果你还不知道虚…...
网站建设系统多少钱/竞价推广培训课程
项目访问 localhost:8080 端口根据自己的环境变动 不要项目名访问。 默认密码123456 MD5Util加密方式 #eclipse 修改访问地址不带项目名称 :https://blog.csdn.net/mengxiangxingdong/article/details/80623522 # 毕业设计 课程设计 #ssm学生宿舍管理系统 #项目…...
设计师网址导航sdc/泰州seo排名扣费
米琪卡哇伊看下面一些例子:1)获取子节点:getchildren()等价于child::*>>>doc.getchildren()2)获取当前节点:"."等价于self::node()>>>doc.xpath(".")>>>doc.xpath("self::node()")…...
vb可以做网站吗/seo关键词优化怎么做
课程实验报告 数据结构C语言描述课 程 实 验 报 告专 业 年 级 2012级软件工程课 程 名 称 数据结构C语言描述指 导 教 师 申红婷学 生 姓 名 王晓霞学 号 20122205041002实 验 日 期 2012.11.7实 验 地 点 A3笃行楼A栋306实 验 成 绩教务处制2013年10月07日实验项目名称栈和队…...
wordpress 清理垃圾插件/贵港网站seo
在外部表的文件中,若文件中的字段由ctrlF来分割,由于ctrlF分隔符在中文后面无法被识别,使得外部表导入出现问题,解决办法是在外部表的文件中,若文件中的字段由ctrlF来分割,由于ctrlF分隔符在中文后面无法被…...
wordpress的d8主题/成功的网络营销案例及分析
树莓派4B踩坑指南 - (15)搭建在线python IDE绝地求生卡盟写在前面Java8中内置了一些在开发中常用的函数式接口,极大的提高了我们的开发效率。那么,问题来了,你知道都有哪些函数式接口吗?函数式接口总览这里,我使用表格…...