零基础设计模式——结构型模式 - 桥接模式

news2025/5/29 1:15:15

第三部分:结构型模式 - 桥接模式 (Bridge Pattern)

在学习了适配器模式如何解决接口不兼容问题后,我们来看看桥接模式。桥接模式是一种更侧重于系统设计的模式,它旨在将抽象部分与其实现部分分离,使它们可以独立地变化。

  • 核心思想:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式 (Bridge Pattern)

“将抽象部分与它的实现部分分离,使它们都可以独立地变化。” (Decouple an abstraction from its implementation so that the two can vary independently.)

想象一下,你正在开发一个跨平台的图形绘制程序。你有不同的形状(如圆形、矩形、三角形),这些形状需要在不同的操作系统上绘制(如Windows、Linux、macOS)。

  • 抽象部分 (Abstraction):形状(Circle, Rectangle)。它们有 draw() 这样的抽象操作。
  • 实现部分 (Implementation):绘制API(WindowsAPI, LinuxAPI, MacOSAPI)。它们负责实际在特定平台上绘制像素点、线条等。

如果直接让每个形状去适配每个平台的API,比如 WindowsCircle, LinuxCircle, MacCircle, WindowsRectangle… 这样会导致类的数量急剧膨胀(形状数量 x 平台数量),并且每当增加一种新形状或支持一个新平台时,都需要创建大量新类,维护困难。

桥接模式通过引入一个“桥梁”来连接抽象部分和实现部分,使得它们可以各自独立地演化。

1. 目的 (Intent)

桥接模式的主要目的:

  1. 解耦抽象和实现:这是核心目的。将一个对象的抽象(它能做什么)与其具体实现(它如何做)分离开来。
  2. 独立变化:使得抽象部分和实现部分可以沿着各自的维度独立地扩展和修改,而不会相互影响。
  3. 提高可扩展性:当需要增加新的抽象或新的实现时,可以更容易地进行,而不需要修改已有的代码或产生大量的子类。

2. 生活中的例子 (Real-world Analogy)

  • 遥控器与电视机

    • 抽象部分 (Abstraction):遥控器(RemoteControl)。它有 turnOn(), turnOff(), changeChannel() 等操作。
    • 具体抽象 (RefinedAbstraction):不同品牌的遥控器,比如 SonyRemoteControl, SamsungRemoteControl。它们可能有一些特定的按钮或功能,但都基于通用的遥控操作。
    • 实现部分 (Implementor):电视机设备接口(TV)。它定义了电视机实际执行操作的接口,如 powerOn(), powerOff(), tuneChannel()
    • 具体实现 (ConcreteImplementor):不同品牌的电视机,比如 SonyTV, SamsungTV。它们各自实现了 TV 接口。
      遥控器(抽象)通过一个“桥梁”(持有 TV 接口的引用)来控制电视机(实现)。你可以用一个通用的索尼遥控器去控制不同型号的索尼电视,也可以为新的电视品牌设计新的电视实现,而遥控器的设计基本不变。
  • 开关与电器

    • 抽象部分:开关(Switch)。它有 on()off() 方法。
    • 具体抽象:不同类型的开关,如墙壁开关(WallSwitch)、遥控开关(RemoteSwitch)。
    • 实现部分:电器接口(Appliance)。它定义了电器被操作的接口,如 turnOnAppliance(), turnOffAppliance()
    • 具体实现:不同类型的电器,如灯(Light)、风扇(Fan)。
      开关通过桥梁控制电器。一个墙壁开关可以控制灯,也可以控制风扇。如果增加一种新的电器(如空调),只需要实现 Appliance 接口,现有的开关类型仍然可用。
  • 不同类型的画笔和颜色

    • 抽象部分:画笔(Pen)。它有 drawShape() 方法。
    • 具体抽象:毛笔(BrushPen)、蜡笔(CrayonPen)。
    • 实现部分:颜色接口(Color)。它定义了 applyColor() 方法。
    • 具体实现:红色(RedColor)、蓝色(BlueColor)。
      画笔(抽象)使用颜色(实现)来绘制。你可以用毛笔蘸红色画,也可以用毛BIC笔蘸蓝色画。增加新的画笔类型或新的颜色都相对独立。

3. 结构 (Structure)

桥接模式通常包含以下角色:

  1. Abstraction (抽象类):定义抽象类的接口,并维护一个指向 Implementor 类型对象的指针或引用。
  2. RefinedAbstraction (扩充抽象类/精确抽象):扩展 Abstraction 定义的接口,实现更具体的操作。这些操作通常会调用 Implementor 的方法。
  3. Implementor (实现类接口):定义实现类的接口,该接口不一定要与 Abstraction 的接口完全一致。Implementor 接口仅提供基本操作,而 Abstraction 则定义了基于这些基本操作的较高层次的操作。
  4. ConcreteImplementor (具体实现类):实现 Implementor 接口并给出具体实现。
    在这里插入图片描述

工作流程

  • 客户端创建一个 ConcreteImplementor 对象和一个 RefinedAbstraction 对象。
  • RefinedAbstraction 对象在构造时会持有一个 ConcreteImplementor 对象的引用(通过 Implementor 接口)。
  • 当客户端调用 RefinedAbstraction 的操作时,这些操作会委托给其持有的 Implementor 对象去执行实际的底层操作。

4. 适用场景 (When to Use)

  • 当你不希望在抽象和它的实现部分之间有一个固定的绑定关系时。例如,当需要在运行时选择或切换实现部分。
  • 当类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充时。这时桥接模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
  • 当对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
  • 当一个类存在两个或多个独立变化的维度,并且你希望避免使用继承导致子类数量爆炸式增长时。这是桥接模式最典型的应用场景。
  • 你想在几个独立扩展的维度上共享一个实现。例如,多个GUI窗口(抽象)可能共享同一个底层的绘图API(实现)。

5. 优缺点 (Pros and Cons)

优点:

  1. 分离抽象和实现:使得抽象和实现可以独立地变化和演化,降低了耦合度。
  2. 提高可扩展性:可以独立地扩展抽象部分和实现部分,而不会相互影响。例如,增加一个新的 RefinedAbstraction 或一个新的 ConcreteImplementor 都很方便。
  3. 符合开闭原则:对扩展开放,对修改关闭。增加新的抽象或实现通常不需要修改现有代码。
  4. 客户端代码更稳定:客户端依赖于抽象接口,实现细节的改变对客户端透明。

缺点:

  1. 增加了系统的理解和设计难度:引入了额外的抽象层和间接性,可能会使系统更难理解。
  2. 可能导致类的数量增加:虽然避免了笛卡尔积式的类爆炸,但仍然需要创建抽象、具体抽象、实现接口、具体实现等多个类。

6. 实现方式 (Implementations)

让我们通过一个“消息发送”的例子来说明桥接模式。我们有不同类型的消息(如文本消息、图片消息),这些消息需要通过不同的方式发送(如邮件、短信、App推送)。

实现类接口 (MessageSender - Implementor)
// message_sender.go (Implementor)
package messaging

// MessageSender 实现类接口
type MessageSender interface {
	Send(messageContent string, recipient string) error
}
// MessageSender.java (Implementor)
package com.example.messaging.sender;

// 实现类接口
public interface MessageSender {
    void sendMessage(String messageContent, String recipient);
}
具体实现类 (EmailSender, SMSSender - ConcreteImplementor)
// email_sender.go (ConcreteImplementor)
package messaging

import "fmt"

// EmailSender 具体实现类
type EmailSender struct{}

func (s *EmailSender) Send(messageContent string, recipient string) error {
	fmt.Printf("Sending EMAIL to %s: %s\n", recipient, messageContent)
	// 实际的邮件发送逻辑
	return nil
}

// sms_sender.go (ConcreteImplementor)
package messaging

import "fmt"

// SMSSender 具体实现类
type SMSSender struct{}

func (s *SMSSender) Send(messageContent string, recipient string) error {
	fmt.Printf("Sending SMS to %s: %s\n", recipient, messageContent)
	// 实际的短信发送逻辑
	return nil
}
// EmailSender.java (ConcreteImplementor)
package com.example.messaging.sender;

public class EmailSender implements MessageSender {
    @Override
    public void sendMessage(String messageContent, String recipient) {
        System.out.printf("Sending EMAIL to %s: %s%n", recipient, messageContent);
        // 实际的邮件发送逻辑
    }
}

// SMSSender.java (ConcreteImplementor)
package com.example.messaging.sender;

public class SMSSender implements MessageSender {
    @Override
    public void sendMessage(String messageContent, String recipient) {
        System.out.printf("Sending SMS to %s: %s%n", recipient, messageContent);
        // 实际的短信发送逻辑
    }
}
抽象类 (Notification - Abstraction)
// notification.go (Abstraction)
package messaging

// Notification 抽象类
type Notification struct {
	sender MessageSender // 持有实现部分的引用
}

func NewNotification(sender MessageSender) *Notification {
	return &Notification{sender: sender}
}

// SetSender 允许动态改变发送方式
func (n *Notification) SetSender(sender MessageSender) {
    n.sender = sender
}

// Notify 抽象方法,由子类实现具体的消息格式化和发送调用
// 注意:在Go中,通常通过接口组合或嵌入来实现类似抽象类的效果,
// 或者让具体通知类型直接持有sender并实现一个通用接口。
// 这里为了更贴合经典UML结构,我们让具体通知类型持有sender。
// 或者,我们可以定义一个 Notify 方法,它接收格式化后的消息。
func (n *Notification) SendNotification(formattedMessage string, recipient string) error {
    if n.sender == nil {
        return fmt.Errorf("sender not set")
    }
	return n.sender.Send(formattedMessage, recipient)
}
// Notification.java (Abstraction)
package com.example.messaging.notification;

import com.example.messaging.sender.MessageSender;

// 抽象类
public abstract class Notification {
    protected MessageSender sender; // 持有实现部分的引用

    public Notification(MessageSender sender) {
        System.out.println("Notification: Initialized with sender: " + sender.getClass().getSimpleName());
        this.sender = sender;
    }

    // 允许动态改变发送方式
    public void setSender(MessageSender sender) {
        System.out.println("Notification: Sender changed to: " + sender.getClass().getSimpleName());
        this.sender = sender;
    }

    // 抽象方法,由子类实现具体的消息格式化和发送调用
    public abstract void notifyUser(String message, String recipient);
}
扩充抽象类 (TextMessage, ImageMessage - RefinedAbstraction)
// text_message.go (RefinedAbstraction)
package messaging

import "fmt"

// TextNotification 精确抽象
type TextNotification struct {
	Notification // 嵌入 Notification,继承其字段和方法
}

func NewTextNotification(sender MessageSender) *TextNotification {
	return &TextNotification{
		Notification: *NewNotification(sender),
	}
}

func (tn *TextNotification) Send(text string, recipient string) error {
	formattedMessage := fmt.Sprintf("[Text]: %s", text)
	fmt.Printf("TextNotification: Formatting message for %s\n", recipient)
	return tn.SendNotification(formattedMessage, recipient) // 调用父类(嵌入结构)的方法
}

// image_message.go (RefinedAbstraction)
package messaging

import "fmt"

// ImageNotification 精确抽象
type ImageNotification struct {
	Notification
}

func NewImageNotification(sender MessageSender) *ImageNotification {
	return &ImageNotification{
		Notification: *NewNotification(sender),
	}
}

func (in *ImageNotification) Send(imageURL string, caption string, recipient string) error {
	formattedMessage := fmt.Sprintf("[Image]: URL=%s, Caption='%s'", imageURL, caption)
	fmt.Printf("ImageNotification: Formatting message for %s\n", recipient)
	return in.SendNotification(formattedMessage, recipient)
}
// TextNotification.java (RefinedAbstraction)
package com.example.messaging.notification;

import com.example.messaging.sender.MessageSender;

public class TextNotification extends Notification {
    public TextNotification(MessageSender sender) {
        super(sender);
        System.out.println("TextNotification: Created.");
    }

    @Override
    public void notifyUser(String message, String recipient) {
        String formattedMessage = "[Text]: " + message;
        System.out.println("TextNotification: Formatting message for " + recipient);
        sender.sendMessage(formattedMessage, recipient); // 调用实现部分的方法
    }
}

// ImageNotification.java (RefinedAbstraction)
package com.example.messaging.notification;

import com.example.messaging.sender.MessageSender;

public class ImageNotification extends Notification {
    private String imageURL;

    public ImageNotification(MessageSender sender, String imageURL) {
        super(sender);
        this.imageURL = imageURL;
        System.out.println("ImageNotification: Created with URL: " + imageURL);
    }

    @Override
    public void notifyUser(String caption, String recipient) {
        String formattedMessage = String.format("[Image]: URL=%s, Caption='%s'", this.imageURL, caption);
        System.out.println("ImageNotification: Formatting message for " + recipient);
        sender.sendMessage(formattedMessage, recipient);
    }
}
客户端使用
// main.go (示例用法)
/*
package main

import (
	"./messaging"
	"fmt"
)

func main() {
	// 创建实现部分的对象
	emailSender := &messaging.EmailSender{}
	smsSender := &messaging.SMSSender{}

	// 1. 发送文本消息通过邮件
	fmt.Println("--- Sending Text via Email ---")
	textMail := messaging.NewTextNotification(emailSender)
	err := textMail.Send("Hello from Go Bridge Pattern!", "user1@example.com")
	if err != nil {
		fmt.Println("Error:", err)
	}

	fmt.Println("\n--- Sending Image via SMS ---")
	// 2. 发送图片消息通过短信
	imageSMS := messaging.NewImageNotification(smsSender)
	err = imageSMS.Send("http://example.com/image.jpg", "Check out this cool pic!", "+1234567890")
	if err != nil {
		fmt.Println("Error:", err)
	}

	fmt.Println("\n--- Sending Text via SMS (changing sender for existing notification object) ---")
	// 3. 同一个文本消息对象,改变发送方式为短信
	textMail.SetSender(smsSender) // 动态改变实现部分
	err = textMail.Send("This is a test text message via SMS now!", "+0987654321")
	if err != nil {
		fmt.Println("Error:", err)
	}

    // 4. 增加新的发送方式 (e.g., AppPushSender)
    //    只需要创建 AppPushSender struct 实现 MessageSender 接口
    //    appPushSender := &messaging.AppPushSender{}
    //    textPush := messaging.NewTextNotification(appPushSender)
    //    textPush.Send("Push notification content", "userID123")

    // 5. 增加新的消息类型 (e.g., VideoNotification)
    //    只需要创建 VideoNotification struct 嵌入 Notification
    //    并实现其特定的 Send 方法 (如 Send(videoURL, title, recipient))
    //    videoMail := messaging.NewVideoNotification(emailSender)
    //    videoMail.Send("http://example.com/video.mp4", "My new video", "friend@example.com")
}
*/
// Main.java (示例用法)
/*
package com.example;

import com.example.messaging.notification.ImageNotification;
import com.example.messaging.notification.Notification;
import com.example.messaging.notification.TextNotification;
import com.example.messaging.sender.EmailSender;
import com.example.messaging.sender.MessageSender;
import com.example.messaging.sender.SMSSender;
// import com.example.messaging.sender.AppPushSender; // 假设新增了App推送

public class Main {
    public static void main(String[] args) {
        // 创建实现部分的对象
        MessageSender emailSender = new EmailSender();
        MessageSender smsSender = new SMSSender();
        // MessageSender appPushSender = new AppPushSender(); // 新增的发送方式

        System.out.println("--- Sending Text via Email ---");
        // 1. 发送文本消息通过邮件
        Notification textMail = new TextNotification(emailSender);
        textMail.notifyUser("Hello from Java Bridge Pattern!", "user1@example.com");

        System.out.println("\n--- Sending Image via SMS ---");
        // 2. 发送图片消息通过短信
        Notification imageSMS = new ImageNotification(smsSender, "http://example.com/image.jpg");
        imageSMS.notifyUser("Check out this cool pic!", "+1234567890");

        System.out.println("\n--- Sending Text via SMS (changing sender for existing notification object) ---");
        // 3. 同一个文本消息对象,改变发送方式为短信
        textMail.setSender(smsSender); // 动态改变实现部分
        textMail.notifyUser("This is a test text message via SMS now!", "+0987654321");

        // System.out.println("\n--- Sending Text via App Push ---");
        // 4. 使用新的发送方式 (如果 AppPushSender 已添加)
        // Notification textPush = new TextNotification(appPushSender);
        // textPush.notifyUser("This is an App Push notification!", "userID789");

        // 5. 增加新的消息类型 (e.g., VideoNotification)
        //    只需要创建 VideoNotification class 继承 Notification
        //    并实现其特定的 notifyUser 方法
        //    Notification videoEmail = new VideoNotification(emailSender, "http://example.com/video.mp4");
        //    videoEmail.notifyUser("My new video", "friend@example.com");
    }
}
*/

7. 与适配器模式的区别

桥接模式和适配器模式都用于处理不同类之间的协作,但它们的意图和结构不同:

  • 意图 (Intent)

    • 适配器模式:改变一个已有类的接口,使其适配另一个接口。通常是解决两个不兼容接口的问题,属于“事后补救”。
    • 桥接模式:将抽象和实现分离,使它们可以独立变化。通常是在设计阶段就考虑分离多个变化维度,属于“事前设计”。
  • 结构 (Structure)

    • 适配器模式通常涉及三个角色:目标接口、适配器、被适配者。
    • 桥接模式通常涉及四个角色:抽象、精确抽象、实现者接口、具体实现者。
  • 解决的问题

    • 适配器解决的是“如何让两个不兼容的接口协同工作”。
    • 桥接解决的是“如何避免因多个独立变化的维度导致类的数量爆炸,并使这些维度能独立演化”。

简单来说,适配器是让“旧的”能“用起来”,桥接是让“新的”能“灵活组合和扩展”。

8. 总结

桥接模式是一种强大的结构型模式,它通过将抽象和实现分离到不同的类层次结构中,使得它们可以独立地进行扩展和修改。这对于处理具有多个变化维度的系统非常有用,可以有效地控制类的数量,提高系统的灵活性和可维护性。

记住它的核心:分离抽象与实现,独立变化

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

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

相关文章

SpringBoot3集成Oauth2.1——4集成Swagger/OpenAPI3

文章目录 访问在线文档页面配置OpenApiConfig 在我之前的文章中&#xff0c;写了 SpringBoot3集成OpenAPI3(解决Boot2升级Boot3) 访问在线文档页面 当我们同样在SpringBoot3使用oauth2.1也就是我之前的文章中写的。现在我们要处理下面这两个的问题了。 <!-- 使用springdoc…

基于深度学习的情绪识别检测系统【完整版】

最近很多小伙伴都在咨询&#xff0c;关于基于深度学习和神经网络算法的情绪识别检测系统。回顾往期文章【点击这里】&#xff0c;介绍了关于人脸数据的预处理和模型训练&#xff0c;这里就不在赘述。今天&#xff0c;将详细讲解如何从零基础手写情绪检测算法和情绪检测系统。主…

Redis学习打卡-Day7-高可用(下)

前面提到&#xff0c;在某些场景下&#xff0c;单实例存Redis缓存会存在的几个问题&#xff1a; 写并发&#xff1a;Redis单实例读写分离可以解决读操作的负载均衡&#xff0c;但对于写操作&#xff0c;仍然是全部落在了master节点上面&#xff0c;在海量数据高并发场景&#x…

博奥龙Nanoantibody系列IP专用抗体

货号名称BDAA0260 HRP-Nanoantibody anti Mouse for IP BDAA0261 AbBox Fluor 680-Nanoantibody anti Mouse for IP BDAA0262 AbBox Fluor 800-Nanoantibody anti Mouse for IP ——无轻/重链干扰&#xff0c;更高亲和力和特异性 01Nanoantibody系列抗体 是利用噬菌体展示纳…

[IMX] 08.RTC 时钟

代码链接&#xff1a;GitHub - maoxiaoxian/imx 目录 1.IMX 的 SNVS 模块 2.SNVS 模块的寄存器 2.1.命令寄存器 - SNVS_HPCOMR 2.2.低功耗控制寄存器 - SNVS_LPCR 2.3.HP 模式的计数寄存器 MSB - SNVS_HPRTCMR 2.4.HP 模式的计数寄存器 LSB - SNVS_HPRTCLR 2.5.LP 模式的…

PG Craft靶机复现 宏macro攻击

一. 端口扫描 只有80端口开启 二. 网页查看 目录扫描一下&#xff1a; dirsearch -u http://192.168.131.169/ 发现 http://192.168.131.169/upload.php 网站书使用xampp搭建&#xff0c;暴露了路径 还发现上传文件 http://192.168.131.169/uploads/ 发现一个上传点&#x…

ElasticSearch--DSL查询语句

ElasticSearch DSL查询文档 分类 查询类型功能描述典型应用场景示例语法查询所有匹配所有文档&#xff0c;无过滤条件数据预览/测试json { "query": { "match_all": {} } }全文检索查询对文本字段分词后匹配&#xff0c;基于倒排索引搜索框模糊匹配、多字段…

Redis(四) - 使用Python操作Redis详解

文章目录 前言一、下载Python插件二、创建项目三、安装 redis 库四、新建python软件包五、键操作六、字符串操作七、列表操作八、集合操作九、哈希表操作十、有序集合操作十一、完整代码1. 完整代码2. 项目下载 前言 本文是基于 Python 操作 Redis 数据库的实战指南&#xff0…

服务器并发实现的五种方法

文章目录 前言一、单线程 / 进程二、多进程并发三、多线程并发四、IO多路转接&#xff08;复用&#xff09;select五、IO多路转接&#xff08;复用&#xff09;poll六、IO多路转接&#xff08;复用&#xff09;epoll 前言 关于网络编程相关知识可看我之前写过的文章&#xff1…

新能源汽车移动充电服务:如何通过智能调度提升充电桩可用率?

随着新能源汽车的普及&#xff0c;充电需求激增&#xff0c;但固定充电桩的布局难以满足用户灵活补能的需求&#xff0c;尤其在高峰时段或偏远地区&#xff0c;"充电难"问题日益凸显。移动充电服务作为新兴解决方案&#xff0c;通过动态调度充电资源&#xff0c;有望…

SpringCloud Alibaba微服务-- Sentinel的使用(笔记)

雪崩问题&#xff1a; 小问题引发大问题&#xff0c;小服务出现故障&#xff0c;处理不当&#xff0c;可能导致整个微服务宕机。 假如商品服务出故障&#xff0c;购物车调用该服务&#xff0c;则可能出现处理时间过长&#xff0c;如果一秒几十个请求&#xff0c;那么处理时间过…

PARSCALE:大语言模型的第三种扩展范式

----->更多内容&#xff0c;请移步“鲁班秘笈”&#xff01;&#xff01;<----- 随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为推动机器智能向通用人工智能&#xff08;AGI&#xff09;迈进的核心驱动力。然而&#xff0c;传统的…

在Windows上,将 Ubuntu WSL 安装并迁移到 D 盘完整教程(含 Appx 安装与迁移导入)

&#x1f4bb; 将 Ubuntu WSL 安装并迁移到 D 盘完整教程&#xff08;含 Appx 安装与迁移导入&#xff09; 本文记录如何在 Windows 系统中手动启用 WSL、下载 Ubuntu 安装包、安装并迁移 Ubuntu 到 D 盘&#xff0c;避免默认写入 C 盘&#xff0c;提高系统性能与可维护性。 ✅…

企微获取会话内容,RSA 解密函数

企微获取会话内容&#xff0c;RSA 解密函数 企微获取会话内容下载SDKSDK配置解密过程解密代码参考SDK文件上传到服务器最后 企微获取会话内容 官方文档&#xff1a; https://developer.work.weixin.qq.com/document/path/91774 下载SDK 根据自己的环境下载对应的SDK。 SDK配置…

MyBatis入门:快速搭建数据库操作框架 + 增删改查(CRUD)

一、创建Mybatis的项目 Mybatis 是⼀个持久层框架, 具体的数据存储和数据操作还是在MySQL中操作的, 所以需要添加MySQL驱动 1.添加依赖 或者 手动添加依赖 <!--Mybatis 依赖包--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactI…

离线安装Microsoft 照片【笔记】

实验环境为&#xff1a;Windows 10 企业版 LTSC。 1.下载好相关离线依赖包和安装包。 2.管理员身份运行powershell&#xff0c;输入以下命令行&#xff1a; Add-AppPackage .\Microsoft.UI.Xaml.2.4_2.42007.9001.0_x64__8wekyb3d8bbwe.Appx Add-AppPackage .\Microsoft.NET…

【后端高阶面经:Elasticsearch篇】39、Elasticsearch 查询性能优化:分页、冷热分离与 JVM 调优

一、索引设计优化:构建高效查询的基石 (一)分片与副本的黄金配置 1. 分片数量计算模型 # 分片数计算公式(单分片建议30-50GB) def calculate_shards(total_data_gb, single_shard_gb=30):return max

基于 ZU49DR FPGA 的无线电射频数据采样转换开发平台核心板

无线电射频数据采样转换开发板及配套开发平台的核心板&#xff0c;该SOM核心板是一个最小系统&#xff0c;包括AMD公司的 Zynq UltraScale RFSOC 第3代系列XCZU49DR-2FFVF1760I FPGA、时钟、电源、内存以及 Flash。与其配套的底板是标准的全高全长Gen4.0 x8的PCIE卡&#xff0c…

软考 系统架构设计师系列知识点之杂项集萃(69)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;68&#xff09; 第114题 若对关系R(A&#xff0c;B&#xff0c;C&#xff0c;D)和S(C&#xff0c;D&#xff0c;E)进行关系代数运算&#xff0c;则表达式 与&#xff08;&#xff09;等价。 A.…

leetcode 83和84 Remove Duplicates from Sorted List 和leetcode 1836

目录 83. Remove Duplicates from Sorted List 82. Remove Duplicates from Sorted List II 1836. Remove Duplicates From an Unsorted Linked List 删除链表中的结点合集 83. Remove Duplicates from Sorted List 代码&#xff1a; /*** Definition for singly-linked l…