模式组合应用-桥接模式(一)

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!

文章为设计模式间的组合使用,涉及代码较多,个人觉得熟能生巧,希望自己能从中学习到新的思维、新的灵感,希望大家也能有所收获。


模式回顾

定义

桥接模式是一种结构型涉及模式,它旨在将抽象部分与其实现部分分离,使得它们可以独立的变化。 桥接模式通过组合而非继承的方式,将抽象和实现解耦,从而解决了传统继承在多维度变化时导致的类爆炸问题。

角色

  • 抽象化(Abstraction): 定义抽象类的接口,并维护一个指向实现化对象的引用。通常是一个抽象类,持有 实现化的引用。

  • 扩展抽象化(Refined Abstraction): 是抽象化角色的具体实现,它扩展了抽象化接口,并可以调用实现化对象的具体方法。

  • 实现化(Implementor): 定义实现类的接口,这个接口不一定要与抽象化接口完全一致,它只给出实现化角色的基本操作。通常是一个接口。

  • 具体实现化(Concrete Implementor): 是实现化角色的具体实现,实现了接口定义的操作。

思考方向

  1. 是否存在多维度变化: 如果一个类存在两个或多个独立变化的维度,并且这些维度都需要独立扩展。

  2. 避免类爆炸: 当使用继承会导致类的数量急剧增加时,桥接模式可以有效的减少类的数量,简化系统结构。

  3. 抽象与实现是都需要独立演化: 如果抽象和实现需要独立的进行修改、扩展或重用。

  4. 运行时切换实现: 桥接模式允许在运行时动态的切换实现,因为抽象和实现是通过组合而不是继承来关联的。

桥接模式代码练习

案例

假设需要开发一个消息通知模块,需满足两个基本要求,第一个要求包含多种发送方式(如: 短信、邮件、APP内部推送),第二个要求是需要区分消息的类型(不同的类型展现形式也不同,如:紧急通知,普通通知、错误消息等)。

案例分析

以桥接模式为代码开发的主要框架,以消息的类型为抽象部分,发送的方式为实现部分,使得两个维度的变化相对独立。

代码

实现部分接口类
public interface MessageSender {void send(String message);}
  • MessageSender为消息发送接口类,定义实现部分的基本功能,包含一个 send() 方法。

具体实现类
/*** 短信发送*/
public class SmsSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("通过短信发送: " + message);}}/*** 邮件发送*/
public class EmailSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("通过邮件发送: " + message);}}/*** APP推送*/
public class AppPushSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("通过APP推送: " + message);}}
  • SmsSender EmailSender AppPushSender 实现部分的具体实现类,实现了 send() 接口方法,并编写各自发送方式。

抽象部分类
public abstract class AbstractMessage {protected MessageSender messageSender;protected AbstractMessage(MessageSender messageSender) {this.messageSender = messageSender;}public abstract void send(String message);
}
  • AbstractMessage 为抽象部分,抽象类中包含实现部分对象的引用 messageSender,并定义了一个 send() 方法,用于描述消息内容的类型。

抽象部分扩展类
/*** 错误消息*/
public class ErrorMessage extends AbstractMessage {protected ErrorMessage(MessageSender messageSender) {super(messageSender);}@Overridepublic void send(String message) {messageSender.send("[错误]" + message + " 系统需要检查!");}
}/*** 普通消息*/
public class NormalMessage extends AbstractMessage {protected NormalMessage(MessageSender messageSender) {super(messageSender);}@Overridepublic void send(String message) {messageSender.send("[普通]" + message);}}/*** 紧急消息*/
public class UrgentMessage extends AbstractMessage {protected UrgentMessage(MessageSender messageSender) {super(messageSender);}@Overridepublic void send(String message) {System.out.println("[紧急]" + message + "请立即处理!");}
}
  • ErrorMessage NormalMessage UrgentMessage 为抽象部分的扩展类,继承了抽象部分类,并对抽象方法进行实现,定义各自消息类型的格式。

测试类
    @Testpublic void test_send() {MessageSender emailSend = new EmailSender();MessageSender smsSend = new SmsSender();MessageSender appPush = new AppPushSender();AbstractMessage normalEmail = new NormalMessage(emailSend);AbstractMessage urgentSms = new UrgentMessage(smsSend);AbstractMessage errorAppPush = new ErrorMessage(appPush);normalEmail.send("系统升级通知");urgentSms.send("服务器负载过高");errorAppPush.send("数据库连接失败");AbstractMessage normalSms = new NormalMessage(smsSend);normalSms.send("每日报告已生成");}
运行结果
通过邮件发送: [普通]系统升级通知
[紧急]服务器负载过高请立即处理!
通过APP推送: [错误]数据库连接失败 系统需要检查!
通过短信发送: [普通]每日报告已生成Process finished with exit code 0

以上为桥接模式代码应用的复习,加深印象。


桥接模式+策略模式

策略模式: 定义一系列的算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客而变化。

案例

在一个电商平台中,商品详情页需要支持多种展示方式(例如: 简洁视图、详细视图、画廊视图),同时为了应对不同的促销活动,商品价格 的计算方式页需要灵活切换(例如: 原价、打折价、会员价)。

模式职责

  • 桥接模式: 将商品展示方式(抽象部分)与具体的商品数据渲染(实现部分)解耦。

  • 策略模式: 封装不同的价格计算算法,使它们可以相互替换,而不会影响客户端。

具体设计

桥接模式
  • 抽象部分: ProductView 定义商品视图的抽象类,持有对 ProductRenderer 的引用

  • 扩展抽象部分: SimpleProductViewDeatilProductView 等具体商品视图

  • 实现者接口: ProductRenderer 定义商品数据渲染的接口

  • 具体实现者: HtmlProductRendererJsonProductRenderer 具体渲染器。

策略模式
  • 抽象策略: PriceCalculationStrategy 定义价格计算策略的接口

  • 具体策略: OriginalPriceStrategyDiscountPriceStrategyMemberPricesStrategy 具体价格计算策略。

  • 上下文: Product 持有对 PriceCalculationStrategy 的引用,并提供设置策略的方法。

代码

ProductRenderer 接口
/*** 产品修饰类* <p>* 桥接模式-接口部分*/
public interface ProductRenderer {String render(String productData);
}
  • 桥接模式中的 实现部分接口,定义了渲染商品数据的通用接口。

  • render(String productData)方法接受商品数据并返回渲染后的商品介绍。

实现部分具体实现者
/*** Html 商品渲染* <p>* 桥接模式 - 具体实现者*/
public class HtmlProductRenderer implements ProductRenderer {@Overridepublic String render(String productData) {return "<html><body><h1>" + productData + "</h1></body></html>";}
}/*** Json 商品渲染*/
public class JsonProductRenderer implements ProductRenderer {@Overridepublic String render(String productData) {return "{\"product\":\"" + productData + "\"}";}
}
  • 桥接模式中的 具体实现者,分别实现了将商品数据渲染成HTML格式和JSON格式的逻辑。

ProductView 抽象类
public abstract class ProductView {protected ProductRenderer productRenderer;public ProductView(ProductRenderer productRenderer) {this.productRenderer = productRenderer;}public void setProductRenderer(ProductRenderer productRenderer) {this.productRenderer = productRenderer;}public abstract void display(String productData);
}
  • 桥接模式中的抽象部分,维护一个 ProductRenderer 类型的引用,作为连接抽象和实现 的桥梁。

  • setProductRenderer() 方法允许在运行时,动态改变渲染器。

  • display(String productData)抽象方法,由具体的视图类型实现其展示逻辑。

抽象部分扩展类
/*** 详细商品视图* <p>* 桥接模式-扩展抽象部分*/
public class DetailProductView extends ProductView {public DetailProductView(ProductRenderer productRenderer) {super(productRenderer);}@Overridepublic void display(String productData) {System.out.println("Detail View: " + productRenderer.render("Detailed Product Info: " + productData));}}/*** 简单视图扩展* <p>* 桥接模式-扩展抽象部分*/
public class SimpleProductView extends ProductView {public SimpleProductView(ProductRenderer productRenderer) {super(productRenderer);}@Overridepublic void display(String productData) {System.out.println("Simple View: " + productRenderer.render(productData));}
}
  • 桥接模式中的抽象扩展部分,代表了不同复杂度的商品视图。

  • 通过调用 productRenderer.render()方法,将实际渲染工作委托给了具体 ProductRenderer 实现类。

PriceCalculationStrategy(策略抽象类)
/*** 价格计算策略接口* <p>* 策略模式-抽象策略*/
public interface PriceCalculationStrategy {double calculatePrice(double originalPrice);}
  • 定义了价格计算的通用接口,calculatePrice(double originalPrice) 方法接受原始价格并返回计算后的价格。

策略实现类
/*** 原始价格策略* <p>* 策略模式-具体策略*/
public class OriginalPriceStrategy implements PriceCalculationStrategy {@Overridepublic double calculatePrice(double originalPrice) {System.out.println(" ===原始价格=== ");return originalPrice;}
}/*** 会员价格策略* <p>* 策略模式-具体策略*/
public class MemberPriceStrategy implements PriceCalculationStrategy {/*** 会员折扣价*/private double memberDiscount;public MemberPriceStrategy(double memberDiscount) {this.memberDiscount = memberDiscount;}@Overridepublic double calculatePrice(double originalPrice) {System.out.println("使用会员折扣,折扣价格: " + memberDiscount);return originalPrice - memberDiscount;}
}/*** 折扣价格策略* <p>* 策略模式-具体策略*/
public class DiscountPriceStrategy implements PriceCalculationStrategy {/*** 折扣率*/private double discountRate;public DiscountPriceStrategy(double discountRate) {this.discountRate = discountRate;}@Overridepublic double calculatePrice(double originalPrice) {System.out.println("使用折扣计算折扣价格, 折扣率: " + discountRate);return originalPrice * (1 - discountRate);}
}
  • 策略模式中的具体策略类,对calculatePrice() 方法进行实现,填充了各自的计算逻辑和必须的参数。

Product(上下文)
public class Product {private String name;private double originalPrice;private PriceCalculationStrategy priceStrategy;public Product(String name, double originalPrice, PriceCalculationStrategy priceStrategy) {this.name = name;this.originalPrice = originalPrice;this.priceStrategy = priceStrategy;}public void setPriceStrategy(PriceCalculationStrategy priceStrategy) {this.priceStrategy = priceStrategy;}public String getName() {return name;}public double getCalculatedPrice() {return priceStrategy.calculatePrice(originalPrice);}
}
  • 策略模式中的上下文,持有一个 PriceCalculationStrategy 类型的引用,

  • setPriceStrategy()方法允许在运行时动态设置价格计算策略。

  • getCalculatedPrice() 方法 将价格计算的职责委托给当前设置的 PriceCalculationStrategy实现类。

测试类
    @Testpublic void test_bridge_strategy() {Product product = new Product("Smart Watch", 300.0, new OriginalPriceStrategy());SimpleProductView productViewWithBridge = new SimpleProductView(new HtmlProductRenderer());productViewWithBridge.display(product.getName() + " - Original Price: " + product.getCalculatedPrice());product.setPriceStrategy(new DiscountPriceStrategy(0.2));productViewWithBridge.setProductRenderer(new JsonProductRenderer());productViewWithBridge.display(product.getName() + " - Discounted Price: " + product.getCalculatedPrice());System.out.println("\n 应用会员价格策略和HTML渲染器: ");product.setPriceStrategy(new MemberPriceStrategy(25.0));productViewWithBridge.setProductRenderer(new HtmlProductRenderer());productViewWithBridge.display(product.getName() + "- Member Price: " + product.getCalculatedPrice());}
运行结果
 ===原始价格=== 
Simple View: <html><body><h1>Smart Watch - Original Price: 300.0</h1></body></html>
使用折扣计算折扣价格, 折扣率: 0.2
Simple View: {"product":"Smart Watch - Discounted Price: 240.0"}应用会员价格策略和HTML渲染器: 
使用会员折扣,折扣价格: 25.0
Simple View: <html><body><h1>Smart Watch- Member Price: 275.0</h1></body></html>Process finished with exit code 0

组合优势

  • 独立变化: 桥接模式使得商品视图和数据渲染可以独立变化,策略模式使得价格计算算法可以独立变化,两种设计模式的结合,使得系统在多个维度上都具有高度的灵活性和可扩展性。

  • 高度复用: 渲染器和价格策略都可以被不同的商品视图和商品对象复用。

  • 增强扩展性: 增加新的商品视图类型、新的数据渲染方式或新的价格计算策略,都只需要添加新的类,无需修改现有代码。

桥接模式+抽象工厂模式

提供一个接口,用于创建相关或依赖对象的家族,而无需指定它们的具体类。

案例

ERP系统报表功能需求:

  • 报表类型支持:销售报表 库存报表

  • 导出格式支持:PDF Excel

  • 界面主题选项:经典主题 现代主题

模式职责

  • 桥接模式: 将报表生成逻辑与导出格式实现解耦,使得二者能够独立变化。

  • 抽象工厂模式: 创建主题相关的报表和导出格式对象族(经典主题/现代主题)

具体设计

桥接模式
  • 抽象部分: Peport定义报告的结构,并持有一个 ExportFormat 的引用

  • 扩展抽象部分: InventoryReport SalesReport 分别生成库存报表、销售报表内容

  • 实现部分: ExportFormat 定义 export() 方法

  • 具体实现: PDFExport ExcelExport 分别提供基础 PDF 、Excel 导出逻辑

抽象工厂模式
  • 抽象工厂: ThemeFactory 接口,定义了创建产品族的一系列方法

  • 具体工厂: ClassicThemeFactory ModernThemeFactory分别创建经典主题风格 和 现代主题风格的对象

  • 抽象产品: Peport

  • 具体产品: SalesReport InventoryReport PDFExport ExcelExport

代码

ExportFormat
/*** 导出格式* <p>* 桥接模式-实现接口部分*/
public interface ExportFormat {String export(String content);}
  • 桥接模式中的实现者接口,定义了报表导出的方法,export(String content)方法 接受原始内容,返回添加格式后的内容。

具体实现类
/*** PDF导出实现*/
public class PDFExport implements ExportFormat {@Overridepublic String export(String content) {return "生成PDF文档: " + content;}
}/*** Excel 导出实现*/
public class ExcelExport implements ExportFormat {@Overridepublic String export(String content) {return "生成Excel文件: " + content;}}
  • 桥接模式中的具体实现者,分别将接受的内容导出为 PDF 和 Excel

Report
/*** 报表* <p>* 桥接模式-抽象部分*/
public abstract class Report {protected String title;protected List<String> data;protected ExportFormat exportFormat;public Report(ExportFormat exportFormat) {this.exportFormat = exportFormat;}public void setTitle(String title) {this.title = title;}public void setData(List<String> data) {this.data = data;}public abstract String generate();public String export() {return exportFormat.export(generate());}
}
  • 桥接模式中的抽象部分,维护一个ExportFormat 类型的引用。

  • generate() 方法用于获取导出的数据内容

  • export() 方法委托 ExportFormat 实现类 增加导出的格式内容。

抽象部分扩展类
/*** 库存报表*/
public class InventoryReport extends Report {public InventoryReport(ExportFormat exportFormat) {super(exportFormat);this.title = "库存报表";}@Overridepublic String generate() {StringBuilder content = new StringBuilder(title + ":\n");for (String item : data) {content.append(" * ").append(item).append("\n");}return content.toString();}
}/*** 销售报表*/
public class SalesReport extends Report {public SalesReport(ExportFormat exportFormat) {super(exportFormat);this.title = "销售报表";}@Overridepublic String generate() {StringBuilder content = new StringBuilder(title + ":\n");for (String item : data) {content.append(" - ").append(item).append("\n");}return content.toString();}
}
  • 桥接模式中的抽象部分的扩展,分别用于获取库存 、销售 报表数据。

ThemeFactory(抽象工厂类)
/*** 主题对象* <p>* 抽象工厂*/
public interface ThemeFactory {Report createSalesReport();ExportFormat createPDFExport();Report createInventorReport();ExportFormat createExcelExport();
}
  • 抽象工厂模式中的抽象工厂接口,声明创建相关产品对象的接口。

具体工厂类
/*** 经典主题风格* <p>* 具体工厂*/
public class ClassicThemeFactory implements ThemeFactory {@Overridepublic Report createSalesReport() {return new SalesReport(createPDFExport());}@Overridepublic ExportFormat createPDFExport() {return new PDFExport() {@Overridepublic String export(String content) {return "经典主题 - " + super.export(content) + " (带传统边框)";}};}@Overridepublic Report createInventorReport() {return new InventoryReport(createExcelExport());}@Overridepublic ExportFormat createExcelExport() {return new ExcelExport() {@Overridepublic String export(String content) {return "经典主题 - " + super.export(content) + " (使用传统颜色)";}};}
}/*** 现代主题风格* <p>* 具体工厂*/
public class ModernThemeFactory implements ThemeFactory {@Overridepublic Report createSalesReport() {return new SalesReport(createPDFExport());}@Overridepublic ExportFormat createPDFExport() {return new PDFExport() {@Overridepublic String export(String content) {return "现代主题 - " + super.export(content) + " (简约设计)";}};}@Overridepublic Report createInventorReport() {return new InventoryReport(createExcelExport());}@Overridepublic ExportFormat createExcelExport() {return new ExcelExport() {@Overridepublic String export(String content) {return "现代主题 - " + super.export(content) + " (扁平化风格)";}};}
}
  • 抽象工厂模式中的具体工厂类,分别用于创建经典主题和现代主题风格的导出格式对象族,遵循抽象工厂模式,创建统一风格的主题产品。

测试类
    @Testpublic void test_report() {List<String> salesData = Arrays.asList("产品A: 100件", "产品B: 200件", "产品C: 150件");List<String> inventoryData = Arrays.asList("仓库1: 500件", "仓库2: 300件", "仓库3: 450件");System.out.println(" === ERP系统报表模块 ===");System.out.println();// 使用经典主题System.out.println("【经典主题报表】");ThemeFactory classicFactory = new ClassicThemeFactory();Report classicFactorySalesReport = classicFactory.createSalesReport();classicFactorySalesReport.setData(salesData);System.out.println(classicFactorySalesReport.export());System.out.println();// 使用现代主题System.out.println("【现代主题报表】");ThemeFactory modernFactory = new ModernThemeFactory();// 现代订单报表Report modernFactorySalesReport = modernFactory.createSalesReport();modernFactorySalesReport.setData(salesData);System.out.println(modernFactorySalesReport.export());// 现代库存报表Report modernFactoryInventorReport = modernFactory.createInventorReport();modernFactoryInventorReport.setData(inventoryData);System.out.println(modernFactoryInventorReport.export());}
运行结果
 === ERP系统报表模块 ===【经典主题报表】
经典主题 - 生成PDF文档: 销售报表:- 产品A: 100件- 产品B: 200件- 产品C: 150件(带传统边框)【现代主题报表】
现代主题 - 生成PDF文档: 销售报表:- 产品A: 100件- 产品B: 200件- 产品C: 150件(简约设计)
现代主题 - 生成Excel文件: 库存报表:* 仓库1: 500件* 仓库2: 300件* 仓库3: 450件(扁平化风格)Process finished with exit code 0

组合优势

  • 高度的灵活性: 新增报表类型或导出格式时,可以直接新增对应实现类,无需修改现有代码。新增主图变化扩展 ThemeFactory,新增报表逻辑扩展Report,新增导出格式则扩展ExportFormat

  • 风格的统一性: 每个工厂确保创建的对象属于同一个主题

--

未完,下一篇接着写,代码量比较多。

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

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

相关文章

【clion】visual studio的sln转cmakelist并使用clion构建32位

我想在linux上运行,所以先转为cmake工程 例如可以把exe mfc 部分不构建,这样ubuntu就不用移植。 先转cmakelist,而后clion完成win32的构建,与vs构建对比,验证脚本正确性。 Vcxproj2CMake https://github.com/gns333/Vcxproj2CMake cmakeconverter https://github.com/pave…

MySQL之分区功能

序言 随着业务发展&#xff0c;我们维护的项目数据库中的数据可能会越来越大&#xff0c;那么单张表的数据变多后&#xff0c;接口查询效率可能会变慢&#xff0c;那我们就直接照抄大厂常见的分库分表吗&#xff1f;—— 当然不是的&#xff0c;分库分表不是万能的。 分库分表…

java_spring boot 中使用 log4j2 及 自定义layout设置示例

1. log4j2对比 原始Logback 优势 对于 Spring Boot 3.x&#xff0c;Logback 是默认日志框架&#xff0c;但在高并发、异步日志场景下&#xff0c;Log4j2 通常表现更优。当业务百万级用户、微服务、日志量大时&#xff1a; ✅ 1. Logback&#xff08;默认 Spring Boot 集成&am…

记录Webapi Excel 导出

文章目录1、helper2、control3、前端 axios记录webapi excel 导出File示例.NET8.0 NPOI2.731、helper using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System.Data; using System.IO; /// <summary> /// 导出EXCEL /// </summary> public class Exce…

VPS服务器安全审计方案:从风险评估到防护实施

随着云计算技术的快速发展&#xff0c;VPS服务器已成为企业信息化建设的重要基础设施。随之而来的安全威胁也日益增多&#xff0c;如何通过专业的安全审计方案保障VPS服务器的稳定运行成为关键课题。本文将系统阐述从漏洞扫描到应急响应的全周期安全审计实施策略&#xff0c;帮…

libmicrohttpd 入门

libmicrohttpd 是一个小型的 C 库&#xff0c;用于在项目中嵌入 HTTP 服务器功能。它设计简单、轻量级&#xff0c;适合需要 HTTP 接口但不想要大型 Web 服务器开销的应用程序。 安装 libmicrohttpd Linux 系统 在基于 Debian/Ubuntu 的系统上&#xff1a; bash sudo apt-…

【网络】使用 DNAT 进行负载均衡时,若未配置配套的 SNAT,回包失败

【网络】iptables 1 概念 【网络】iptables 2 查看规则 【网络】使用 DNAT 进行负载均衡时&#xff0c;若未配置配套的 SNAT&#xff0c;回包失败 【网络】回包路由原理 使用 DNAT 进行负载均衡时&#xff0c;若未配置配套的 SNAT&#xff0c;后端服务器将直接回包给客户端&am…

深入解析GCC:从编译原理到嵌入式底层实战

继续更新编译器底层系列&#xff01;&#xff01;&#xff01;硬核C语言的屠龙之术&#xff1a;从GCC到汇编的底层征途&#xff08;一&#xff09;总纲&#xff1a; 恭喜你&#xff0c;决定踏上这条通往嵌入式大佬的硬核之路。这条路的起点&#xff0c;不是C语言的语法书&#…

最新MySQL面试题(2025超详细版)

2025最新超详细MySQL面试题 文章目录2025最新超详细MySQL面试题[toc]一、 SQL 和基本操作1. SQL的执行顺序2. 如何优化MySQL查询3. 常用的聚合函数4. 数据库事务5. 事务的四大特性(ACID)6. 视图7. MySQL中使用LIMIT子句进行分页8. MySQL中使用变量和用户定义的函数9. MySQL中的…

Spring Retry实战指南_让你的应用更具韧性

1 Spring Retry概述 1.1 什么是Spring Retry Spring Retry是Spring生态系统中的一个重要组件,专门用于处理应用程序中的重试逻辑。在分布式系统和微服务架构中,网络通信、外部服务调用、数据库访问等操作都可能因为各种原因而失败,如网络抖动、服务暂时不可用、资源竞争等…

大数据毕业设计选题推荐-基于大数据的1688商品类目关系分析与可视化系统-Hadoop-Spark-数据可视化-BigData

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

【Grafana】grafana-image-renderer配合python脚本实现仪表盘导出pdf

背景 os&#xff1a;centos7Grafana&#xff1a;v12grafana-image-renderer&#xff1a;v4.0.10插件&#xff1a;否grafana-image-renderer可以以插件形式启动&#xff0c;也可以以单独服务启动&#xff0c;在centos7插件启动时&#xff0c;报错glibc版本太低&#xff0c;未找到…

静/动态库 IIC(arm) day58

十七&#xff1a;动态库和静态库 库&#xff1a;一堆可执行二进制文件的集合&#xff0c;由若干个.o文件归并生成 一&#xff1a;静态(链接)库&#xff1a;libxxx.a 生成一个独立的可执行程序(运行时仅需要一个文件即可) 使用方便 不需要安装 文件比较大 多个程序使用同一个静态…

uniapp 手写签名组件开发全攻略

引言在移动应用开发中&#xff0c;手写签名功能是一个常见的需求&#xff0c;特别是在电子合同、审批流程、金融交易等场景中。本文将详细介绍如何基于uni-app框架开发一个高性能、功能丰富的手写签名组件&#xff0c;并分享开发过程中的技术要点和最佳实践。组件概述这个签名组…

理解JavaScript中的函数赋值和调用

&#x1f468; 作者简介&#xff1a;大家好&#xff0c;我是Taro&#xff0c;全栈领域创作者 ✒️ 个人主页&#xff1a;唐璜Taro &#x1f680; 支持我&#xff1a;点赞&#x1f44d;&#x1f4dd; 评论 ⭐️收藏 文章目录前言一、函数赋值二、函数调用三、 代码示例总结前言…

交叉编译 手动安装 SQLite 库 移植ARM

# 下载源码 wget https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz tar -xzf sqlite-autoconf-3420000.tar.gz cd sqlite-autoconf-3420000cd /home/lxh/sqlite-autoconf-3420000 make distclean //清除下&#xff0c;因为我安装失败过。 ./configure --hostarm-…

翻译记忆库(TMX)与机器翻译的结合应用

更多内容请见: 机器翻译修炼-专栏介绍和目录 文章目录 一、核心概念解析 1.1 翻译记忆库 (Translation Memory, TM) 1.2 翻译记忆交换格式 (Translation Memory eXchange, TMX) 二、为何要将两者结合? 2.1 TM和MT的优势是高度互补的 2.2 TMX在结合中的关键作用 2.3 TMX与MT的…

SpringBoot中集成eclipse.paho.client.mqttv3实现mqtt客户端并支持断线重连、线程池高并发改造、存储入库mqsql和redis示例业务流程,附资源下载

场景 SpringBoot整合MQTT服务器实现消息的发送与订阅(推送消息与接收推送)&#xff1a; SpringBoot整合MQTT服务器实现消息的发送与订阅(推送消息与接收推送)_服务端接收mqtt消息-CSDN博客 上面SpringBoot集成MQTT使用的是spring-integration-mqtt依赖&#xff0c;也是经常使…

【考研408数据结构-08】 图论基础:存储结构与遍历算法

&#x1f4da; 【考研408数据结构-08】 图论基础&#xff1a;存储结构与遍历算法 &#x1f3af; 考频&#xff1a;⭐⭐⭐⭐⭐ | 题型&#xff1a;选择题、综合应用题、算法设计题 | 分值&#xff1a;约8-15分 引言 想象你正在规划一次跨省自驾游&#xff0c;面前摊开一张复杂的…

SQL查询语句的执行顺序

好的&#xff0c;我们来详细讲解一下 SQL 查询语句的执行顺序。 很多人会误以为 SQL 的执行顺序就是我们写的顺序&#xff08;SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY&#xff09;&#xff0c;但实际上&#xff0c;数据库引擎在底层处理查询…