零基础设计模式——创建型模式 - 生成器模式

第二部分:创建型模式 - 生成器模式 (Builder Pattern)

前面我们学习了单例、工厂方法和抽象工厂模式,它们都关注如何创建对象。生成器模式(也常被称为建造者模式)是另一种创建型模式,它专注于将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

  • 核心思想:将一个复杂对象的构建层与其表示层分离,使得同样的构建过程可以创建不同的表示。

生成器模式 (Builder Pattern)

“将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的对象表示。”

想象一下去快餐店点餐,比如赛百味 (Subway) 或者定制汉堡店:

  • 复杂对象:你最终得到的定制三明治或汉堡。
  • 构建过程:选择面包类型 -> 选择肉类 -> 选择蔬菜 -> 选择酱料 -> 完成。
  • 表示:
    • 三明治A:全麦面包 + 鸡肉 + 生菜番茄 + 蜂蜜芥末酱。
    • 三明治B:白面包 + 牛肉 + 洋葱青椒 + 西南酱。
    • 汉堡C:芝麻面包 + 双层牛肉饼 + 酸黄瓜 + 特制酱。

服务员(Director)会按照固定的步骤(选择面包、肉、菜、酱)来询问你。你(作为构建指令的提供者,或者说,你指导一个Builder)告诉服务员每一步你的选择。最终,服务员根据你的选择组装出你想要的三明治或汉堡。

这个模式的关键在于,构建过程(点餐步骤)是标准化的,但每一步的具体选择(面包种类、肉类种类等)是灵活的,从而可以产生多种不同的最终产品(表示)。

1. 目的 (Intent)

生成器模式的主要目的:

  1. 封装复杂对象的创建过程:当一个对象的创建过程非常复杂,包含多个步骤或多个部分时,使用生成器模式可以将这个复杂的构建逻辑封装起来。
  2. 分步构建对象:允许你分步骤、按顺序地构建一个对象,而不是一次性通过一个巨大的构造函数来创建。
  3. 创建不同表示:使得同样的构建过程可以创建出内部结构不同(即属性不同)的多种对象表示。
  4. 更好的控制构建过程:Director 控制构建的顺序和步骤,Builder 负责实现每个步骤。

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

  • 组装电脑:

    • Product (产品):一台组装好的电脑。
    • Builder (抽象建造者):ComputerBuilder 接口,定义了安装CPU、主板、内存、硬盘、显卡等步骤的方法。
    • ConcreteBuilder (具体建造者):GamingComputerBuilder (选择高性能CPU、高端显卡、大内存),OfficeComputerBuilder (选择性价比CPU、集成显卡、普通内存)。
    • Director (指挥者):电脑装机员。他按照固定的顺序(先装CPU到主板,再装内存条,再装入机箱…)来指导 Builder 进行组装。他不需要知道具体用的是什么牌子的CPU或显卡,这些由具体的 Builder 决定。
      客户只需要告诉装机员他想要一台“游戏电脑”还是“办公电脑”(选择了哪个 ConcreteBuilder),装机员就能按部就班地组装出来。
  • 编写一份复杂的文档 (如简历、报告):

    • Product:最终的文档。
    • Builder:DocumentBuilder 接口,定义了 buildHeader(), buildBodyParagraph(text), buildListItem(item), buildFooter() 等方法。
    • ConcreteBuilder:ResumeBuilder (简历的页眉是个人信息,页脚是联系方式),ReportBuilder (报告的页眉是标题和日期,页脚是页码)。
    • Director:文档生成程序。它调用 Builder 的方法来按顺序构建文档的各个部分。
  • URL 构建:Java中的 UriComponentsBuilder 或类似工具,允许你分步设置 scheme, host, port, path, query parameters 等来构建一个URL。

3. 结构 (Structure)

生成器模式通常包含以下角色:

  1. Product (产品):表示被构建的复杂对象。ConcreteBuilder 创建该产品的内部表示并定义它的装配过程。
  2. Builder (抽象建造者):为创建一个 Product对象的各个部件指定抽象接口。它通常包含一系列 buildPartX() 方法和一个 getResult() 方法用于返回构建好的产品。
  3. ConcreteBuilder (具体建造者):实现 Builder 接口,构造和装配产品的各个部件。定义并明确它所创建的表示,并提供一个检索产品的接口。
  4. Director (指挥者/导演):构造一个使用 Builder 接口的对象。Director 类负责调用具体建造者角色以创建产品对象。Director 并不保存对具体建造者角色的引用,而是通过其抽象接口与之协作。
    在这里插入图片描述

构建流程:

  1. 客户端创建一个 ConcreteBuilder 对象。
  2. 客户端创建一个 Director 对象,并将 ConcreteBuilder 对象传递给它。
  3. Director 调用 Builder 接口中定义的方法,按特定顺序指导 ConcreteBuilder 构建产品。
  4. ConcreteBuilder 逐步构建产品的内部表示。
  5. 客户端从 ConcreteBuilder 中获取构建完成的 Product。

4. 适用场景 (When to Use)

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。
  • 对象的构建过程非常复杂,包含多个可选步骤或配置。例如,创建一个复杂的配置对象,其中某些配置项是可选的,或者有多种组合方式。
  • 需要分步创建一个对象,并且在每一步之后可能需要进行一些中间操作或验证。
  • 希望隐藏对象的内部表示和构建细节。
  • 一个对象有非常多的构造参数,其中大部分是可选的。如果用构造函数,可能会导致构造函数参数列表过长,或者需要多个重载的构造函数(伸缩构造函数问题)。生成器模式可以提供更优雅的链式调用方式。

5. 优缺点 (Pros and Cons)

优点:

  1. 封装性好:使得客户端不必知道产品内部组成的细节,产品本身和创建过程解耦。
  2. 易于控制构建过程:Director 可以精确控制构建的顺序和步骤。
  3. 可以创建不同表示:同样的构建过程可以应用于不同的 ConcreteBuilder,从而得到不同的产品表示。
  4. 更好的可读性和易用性:对于有很多可选参数的复杂对象,使用链式调用的生成器比使用长参数列表的构造函数更清晰。
  5. 分步构建:可以将产品的构建过程分解为多个独立的步骤,使得构建过程更加灵活。

缺点:

  1. 类的数量增多:需要为每个产品创建一个 ConcreteBuilder 类,如果产品种类很多,会导致类的数量增加。
  2. 产品必须有共同点:生成器模式创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用生成器模式。
  3. 模式本身相对复杂:相比于工厂模式,生成器模式的结构更复杂,包含的角色更多。

6. 实现方式 (Implementations)

让我们通过一个构建“报告文档”的例子来看看生成器模式的实现。报告可以有标题、作者、日期、多个段落内容、以及页脚。

Product (ReportDocument)
// report_document.go
package reportimport ("fmt""strings"
)// ReportDocument 产品
type ReportDocument struct {Title    stringAuthor   stringDate     stringContents []stringFooter   string
}func (rd *ReportDocument) AddContent(paragraph string) {rd.Contents = append(rd.Contents, paragraph)
}func (rd *ReportDocument) Display() {fmt.Println("========================================")if rd.Title != "" {fmt.Printf("Title: %s\n", rd.Title)}if rd.Author != "" {fmt.Printf("Author: %s\n", rd.Author)}if rd.Date != "" {fmt.Printf("Date: %s\n", rd.Date)}fmt.Println("----------------------------------------")for _, content := range rd.Contents {fmt.Println(content)}fmt.Println("----------------------------------------")if rd.Footer != "" {fmt.Printf("Footer: %s\n", rd.Footer)}fmt.Println("========================================")
}
// ReportDocument.java
package com.example.report;import java.util.ArrayList;
import java.util.List;// 产品
public class ReportDocument {private String title;private String author;private String date;private List<String> contents = new ArrayList<>();private String footer;public void setTitle(String title) { this.title = title; }public void setAuthor(String author) { this.author = author; }public void setDate(String date) { this.date = date; }public void addContent(String paragraph) { this.contents.add(paragraph); }public void setFooter(String footer) { this.footer = footer; }public void display() {System.out.println("========================================");if (title != null && !title.isEmpty()) {System.out.println("Title: " + title);}if (author != null && !author.isEmpty()) {System.out.println("Author: " + author);}if (date != null && !date.isEmpty()) {System.out.println("Date: " + date);}System.out.println("----------------------------------------");for (String content : contents) {System.out.println(content);}System.out.println("----------------------------------------");if (footer != null && !footer.isEmpty()) {System.out.println("Footer: " + footer);}System.out.println("========================================");}
}
Builder (ReportBuilder)
// report_builder.go
package report// ReportBuilder 抽象建造者接口
type ReportBuilder interface {SetTitle(title string)SetAuthor(author string)SetDate(date string)AddParagraph(paragraph string)SetFooter(footer string)GetReport() *ReportDocument
}
// ReportBuilder.java
package com.example.report;// 抽象建造者接口
public interface ReportBuilder {void setTitle(String title);void setAuthor(String author);void setDate(String date);void addParagraph(String paragraph);void setFooter(String footer);ReportDocument getReport();
}
ConcreteBuilder (SimpleReportBuilder, DetailedReportBuilder)
// simple_report_builder.go
package report// SimpleReportBuilder 具体建造者 - 构建简单报告
type SimpleReportBuilder struct {document *ReportDocument
}func NewSimpleReportBuilder() *SimpleReportBuilder {return &SimpleReportBuilder{document: &ReportDocument{}}
}func (b *SimpleReportBuilder) SetTitle(title string)               { b.document.Title = "Simple Report: " + title }
func (b *SimpleReportBuilder) SetAuthor(author string)             { /* 简单报告不包含作者 */ }
func (b *SimpleReportBuilder) SetDate(date string)                 { b.document.Date = date }
func (b *SimpleReportBuilder) AddParagraph(paragraph string)       { b.document.AddContent(paragraph) }
func (b *SimpleReportBuilder) SetFooter(footer string)             { b.document.Footer = "End of Simple Report." }
func (b *SimpleReportBuilder) GetReport() *ReportDocument          { return b.document }// detailed_report_builder.go
package report// DetailedReportBuilder 具体建造者 - 构建详细报告
type DetailedReportBuilder struct {document *ReportDocument
}func NewDetailedReportBuilder() *DetailedReportBuilder {return &DetailedReportBuilder{document: &ReportDocument{}}
}func (b *DetailedReportBuilder) SetTitle(title string)         { b.document.Title = "Detailed Analysis: " + title }
func (b *DetailedReportBuilder) SetAuthor(author string)       { b.document.Author = author }
func (b *DetailedReportBuilder) SetDate(date string)           { b.document.Date = "Generated on: " + date }
func (b *DetailedReportBuilder) AddParagraph(paragraph string) { b.document.AddContent("\t- " + paragraph) }
func (b *DetailedReportBuilder) SetFooter(footer string) { b.document.Footer = fmt.Sprintf("Report Concluded. %s. (c) MyCompany", footer)
}
func (b *DetailedReportBuilder) GetReport() *ReportDocument    { return b.document }
// SimpleReportBuilder.java
package com.example.report;// 具体建造者 - 构建简单报告
public class SimpleReportBuilder implements ReportBuilder {private ReportDocument document;public SimpleReportBuilder() {this.document = new ReportDocument();System.out.println("SimpleReportBuilder: Initialized.");}@Overridepublic void setTitle(String title) {document.setTitle("Simple Report: " + title);}@Overridepublic void setAuthor(String author) {// 简单报告不包含作者System.out.println("SimpleReportBuilder: Author field is ignored for simple reports.");}@Overridepublic void setDate(String date) {document.setDate(date);}@Overridepublic void addParagraph(String paragraph) {document.addContent(paragraph);}@Overridepublic void setFooter(String footer) {document.setFooter("End of Simple Report.");}@Overridepublic ReportDocument getReport() {return document;}
}// DetailedReportBuilder.java
package com.example.report;// 具体建造者 - 构建详细报告
public class DetailedReportBuilder implements ReportBuilder {private ReportDocument document;public DetailedReportBuilder() {this.document = new ReportDocument();System.out.println("DetailedReportBuilder: Initialized.");}@Overridepublic void setTitle(String title) {document.setTitle("Detailed Analysis: " + title);}@Overridepublic void setAuthor(String author) {document.setAuthor(author);}@Overridepublic void setDate(String date) {document.setDate("Generated on: " + date);}@Overridepublic void addParagraph(String paragraph) {document.addContent("\t- " + paragraph); // 添加缩进和项目符号}@Overridepublic void setFooter(String footer) {document.setFooter(String.format("Report Concluded. %s. (c) MyCompany", footer));}@Overridepublic ReportDocument getReport() {return document;}
}
Director (ReportDirector)
// report_director.go
package report// ReportDirector 指挥者
type ReportDirector struct {builder ReportBuilder
}func NewReportDirector(builder ReportBuilder) *ReportDirector {return &ReportDirector{builder: builder}
}// ConstructMonthlyReport 指挥构建月度报告
func (d *ReportDirector) ConstructMonthlyReport(title, author, date string, contents []string, footerDetails string) *ReportDocument {d.builder.SetTitle(title)d.builder.SetAuthor(author) // Builder 内部可能忽略此项d.builder.SetDate(date)for _, p := range contents {d.builder.AddParagraph(p)}d.builder.SetFooter(footerDetails)return d.builder.GetReport()
}// ConstructQuickSummary 指挥构建快速摘要
func (d *ReportDirector) ConstructQuickSummary(title, date string, summaryContent string) *ReportDocument {d.builder.SetTitle(title)// 快速摘要可能不需要作者和完整页脚d.builder.SetDate(date)d.builder.AddParagraph(summaryContent)d.builder.SetFooter("Quick Summary") // 简化页脚return d.builder.GetReport()
}
// ReportDirector.java
package com.example.report;import java.util.List;// 指挥者
public class ReportDirector {private ReportBuilder builder;public ReportDirector(ReportBuilder builder) {this.builder = builder;System.out.println("ReportDirector: Configured with builder: " + builder.getClass().getSimpleName());}// 指挥构建月度报告public ReportDocument constructMonthlyReport(String title, String author, String date, List<String> contents, String footerDetails) {System.out.println("ReportDirector: Constructing Monthly Report...");builder.setTitle(title);builder.setAuthor(author); // Builder 内部可能忽略此项builder.setDate(date);for (String p : contents) {builder.addParagraph(p);}builder.setFooter(footerDetails);return builder.getReport();}// 指挥构建快速摘要public ReportDocument constructQuickSummary(String title, String date, String summaryContent) {System.out.println("ReportDirector: Constructing Quick Summary...");builder.setTitle(title);// 快速摘要可能不需要作者和完整页脚builder.setDate(date);builder.addParagraph(summaryContent);builder.setFooter("Quick Summary"); // 简化页脚return builder.getReport();}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("./report""time"
)func main() {// 构建简单月度报告simpleBuilder := report.NewSimpleReportBuilder()director1 := report.NewReportDirector(simpleBuilder)monthlyContents := []string{"Sales are up by 10%.","Customer satisfaction is high.",}simpleMonthlyReport := director1.ConstructMonthlyReport("October Sales","Sales Team", // SimpleBuilder 会忽略作者time.Now().Format("2006-01-02"),monthlyContents,"Internal Use Only",)simpleMonthlyReport.Display()fmt.Println("\n-----------------------------------\n")// 构建详细年度报告detailedBuilder := report.NewDetailedReportBuilder()director2 := report.NewReportDirector(detailedBuilder)annualContents := []string{"Market share increased by 5%.","New product line launched successfully with positive feedback.","Research and Development made significant progress on Project X.",}detailedAnnualReport := director2.ConstructMonthlyReport( // 复用构建逻辑,但用不同的builder"Annual Financials 2023","Dr. Alice Smith, CFO",time.Now().Format("Jan 02, 2006"),annualContents,"For Shareholders",)detailedAnnualReport.Display()fmt.Println("\n-----------------------------------\n")// 使用同一个 detailedBuilder 构建另一种类型的报告 (快速摘要)quickSummary := director2.ConstructQuickSummary("Q3 Highlights",time.Now().Format("2006-01-02"),"Overall positive quarter with key targets met.",)quickSummary.Display()
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.report.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;public class Main {public static void main(String[] args) {String currentDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE);// 构建简单月度报告System.out.println("--- Building Simple Monthly Report ---");ReportBuilder simpleBuilder = new SimpleReportBuilder();ReportDirector director1 = new ReportDirector(simpleBuilder);List<String> monthlyContents = Arrays.asList("Sales are up by 10%.","Customer satisfaction is high.");ReportDocument simpleMonthlyReport = director1.constructMonthlyReport("October Sales","Sales Team", // SimpleBuilder 会忽略作者currentDate,monthlyContents,"Internal Use Only");simpleMonthlyReport.display();System.out.println("\n--- Building Detailed Annual Report ---");// 构建详细年度报告ReportBuilder detailedBuilder = new DetailedReportBuilder();ReportDirector director2 = new ReportDirector(detailedBuilder);List<String> annualContents = Arrays.asList("Market share increased by 5%.","New product line launched successfully with positive feedback.","Research and Development made significant progress on Project X.");ReportDocument detailedAnnualReport = director2.constructMonthlyReport( // 复用构建逻辑,但用不同的builder"Annual Financials 2023","Dr. Alice Smith, CFO",LocalDate.now().format(DateTimeFormatter.ofPattern("MMM dd, yyyy")),annualContents,"For Shareholders");detailedAnnualReport.display();System.out.println("\n--- Building Quick Summary with Detailed Builder ---");// 使用同一个 detailedBuilder 构建另一种类型的报告 (快速摘要)ReportDocument quickSummary = director2.constructQuickSummary("Q3 Highlights",currentDate,"Overall positive quarter with key targets met.");quickSummary.display();}
}
*/
关于链式调用 (Fluent Interface)

在很多现代语言的实现中,Builder 的 buildPartX() 方法通常会返回 this (或 self),以支持链式调用,这样客户端代码可以更简洁。这种情况下,Director 角色有时会被弱化,甚至省略,客户端直接通过链式调用来指导 Builder。

Java 链式调用示例 (不使用显式 Director):

// MailMessage.java (Product)
package com.example.mail;public class MailMessage {private String from;private String to;private String subject;private String body;private String cc;// 私有构造,强制使用Builderprivate MailMessage(Builder builder) {this.from = builder.from;this.to = builder.to;this.subject = builder.subject;this.body = builder.body;this.cc = builder.cc;}@Overridepublic String toString() {return "MailMessage{" +"from='" + from + '\'' +", to='" + to + '\'' +", subject='" + subject + '\'' +", body='" + body + '\'' +(cc != null ? ", cc='" + cc + '\'' : "") +'}';}// 静态内部 Builder 类public static class Builder {private String from;private String to; // 必填private String subject;private String body;private String cc; // 可选public Builder(String to) { // 必填项通过构造函数传入this.to = to;}public Builder from(String from) {this.from = from;return this;}public Builder subject(String subject) {this.subject = subject;return this;}public Builder body(String body) {this.body = body;return this;}public Builder cc(String cc) {this.cc = cc;return this;}public MailMessage build() {if (this.from == null || this.from.isEmpty()) {throw new IllegalStateException("From address cannot be empty");}// 可以在这里添加更多校验逻辑return new MailMessage(this);}}
}// Main.java (示例用法)
/*
package com.example;import com.example.mail.MailMessage;public class Main {public static void main(String[] args) {MailMessage message1 = new MailMessage.Builder("recipient@example.com").from("sender@example.com").subject("Hello from Builder Pattern!").body("This is a demonstration of the fluent builder pattern.").cc("manager@example.com").build();System.out.println(message1);MailMessage message2 = new MailMessage.Builder("another@example.com").from("noreply@example.com").subject("Important Update")// body 和 cc 是可选的.build();System.out.println(message2);try {MailMessage message3 = new MailMessage.Builder("test@example.com")// .from("testsender@example.com") // 故意不设置 from.subject("Test").build();System.out.println(message3);} catch (IllegalStateException e) {System.err.println("Error building message: " + e.getMessage());}}
}
*/

这种链式调用的方式在Java中非常流行,例如 StringBuilder, OkHttp Request.Builder, Lombok @Builder 注解等。

7. 与抽象工厂模式的区别

  • 抽象工厂模式 (Abstract Factory):

    • 关注点:创建产品族 (一系列相关的产品对象)。
    • 产品创建:通常是一次性获取整个产品族中的某个产品 (例如 factory.createButton())。
    • 目的:保证创建出来的产品属于同一个系列,相互兼容。
  • 生成器模式 (Builder):

    • 关注点:创建单个复杂对象,其构建过程包含多个步骤。
    • 产品创建:分步骤构建,最后通过 getResult() 或 build() 获取完整对象。
    • 目的:将复杂对象的构建过程和其表示分离,允许同样的构建过程创建不同的表示。

关键区别:

  • 抽象工厂返回的是多个不同类型的产品(但它们属于一个系列)。
  • 生成器返回的是一个复杂的产品,这个产品是逐步构建起来的。
  • 抽象工厂通常在客户端决定使用哪个具体工厂后,由工厂直接创建出产品。而生成器模式中,Director 控制构建步骤,Builder 实现这些步骤。

有时,生成器模式的 buildPartX() 方法内部可能会使用工厂方法来创建部件。

8. 总结

生成器模式是一种强大的创建型模式,适用于构建具有多个组成部分、构建过程复杂或需要多种表示的复杂对象。它通过将构建过程与对象的表示分离,提高了代码的模块化程度和灵活性。当遇到有很多可选参数的构造函数时,或者当对象的创建逻辑比较复杂时,可以考虑使用生成器模式来简化对象的创建和提高代码的可读性。

记住它的核心:分步构建复杂对象,不同表示。

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

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

相关文章

Debezium快照事件监听器系统设计

Debezium快照事件监听器系统设计 1. 系统概述 1.1 设计目标 为 Debezium 的快照过程提供可扩展的事件监听机制允许外部系统在快照过程中执行自定义逻辑提供线程安全的事件分发机制确保监听器的异常不会影响主快照流程1.2 核心功能 表快照开始事件监听表快照完成事件监听行数据…

手术机器人行业新趋势:Kinova多机械臂协同系统如何突破复杂场景适应性瓶颈?

机器人手术历经多阶段技术演进&#xff0c;已成为现代医疗重要方向。其需求增长源于医疗机构对高精度低风险手术方案的需求、微创手术普及及技术进步带来的复杂场景适应性提升。Kinova 轻型机械臂凭借模块化设计与即插即用功能&#xff0c;可快速适配不同手术环境&#xff0c;为…

力扣面试150题-- 二叉树展开为链表

Day 46 题目描述 思路 初次做法&#xff1a;由于我直接考虑O&#xff08;1&#xff09;级别的空间复杂度&#xff0c;于是采取了以下做法&#xff1a; 接下来的内容就是递归函数如果该节点为空&#xff0c;就返回null将此时的current作为头节点&#xff0c;left和right作为孩…

【Python】开发工具uv

文章目录 1. uv install1.1 下载安装脚本来安装1.2 使用pipx安装uv1.3 补充 2. 考虑在离线系统上安装uv2.1 下载并上传安装包2.2 用户级安装uv&#xff08;~/.local/bin/&#xff09;2.3 补充 3. uv 管理Python解释器4. uv 管理依赖5. uv运行代码5.1 uv不在项目下执行脚本5.2 u…

zabbix批量主机维护脚本兼容性更新

最近做新老版本zabbix监控主机迁移发现zabbix6.0后api安全有了效大升级&#xff0c;批量主机维护脚本出现认证兼容性问题&#xff0c;以下为脚本更新token支持&#xff1a;在这里插入代码片&#xff1a; # /usr/bin/env python3 # -*- coding:utf-8 -*- import requests impor…

Java中static关键字深度解析:从入门到高阶实战

Java中static关键字深度解析&#xff1a;从入门到高阶实战 目录 static的本质与核心特性静态变量 vs 实例变量&#xff1a;底层对比静态方法的设计哲学与应用场景高级用法&#xff1a;突破常规的static技巧 4.1 静态代码块&#xff1a;类加载的“初始化引擎”4.2 静态内部类&…

基于RT-Thread的STM32F4开发第五讲——软件模拟I2C

文章目录 前言一、RT-Thread工程创建二、AT24C02三、函数编写1.I2C_soft.c2.I2C_soft.h3.main.h 四、效果展示五、资源分享总结 前言 本章是基于RT-Thread studio实现软件模拟I2C&#xff0c;开发板是正点原子的STM32F4探索者&#xff0c;使用的RT-Thread驱动是5.1.0&#xff0…

49、c# 能⽤foreach 遍历访问的对象需满足什么条件?

在 C# 中&#xff0c;要使用 foreach 循环遍历一个对象&#xff0c;该对象必须满足以下条件之一&#xff1a; 1. 实现 IEnumerable 或 IEnumerable 接口 非泛型版本&#xff1a;System.Collections.IEnumerable public class MyCollection : IEnumerable {private int[] _da…

推客小程序系统开发:全栈式技术解决方案与行业赋能实践​

​ 在数字化营销深度渗透各行业的当下&#xff0c;传统推广模式已难以满足企业精细化运营与高效获客的需求。专业的推客小程序系统凭借其强大的裂变传播能力与灵活的推广机制&#xff0c;成为企业构建私域流量池、提升推广效能的核心工具。我们基于多年技术沉淀与行业洞察&…

WPF布局系统详解:掌握界面设计的核心艺术

掌握界面设计的核心艺术 1. WPF布局系统概述2. Grid布局详解2.1 基本行列定义2.2 单元格定位与跨行跨列 3. StackPanel布局4. DockPanel布局5. WrapPanel与Canvas5.1 WrapPanel自动换行布局 5. Canvas绝对定位6. 布局嵌套与综合应用7. 布局性能优化8. 响应式布局技巧9. 实战&am…

labview实现LED流水灯的第一种方法

目的&#xff1a;写一个跑马灯程序&#xff0c;7个灯从左到右不停的轮流点亮&#xff0c;闪烁间隔由滑动条调节。 一、方法1&#xff1a;使用顺序结构 使用顺序结构&#xff0c;平铺式顺序结构与创建局部变量实现LED流水灯 具体步骤如下&#xff1a; 第一步&#xff0c;选择…

uniapp如何设置uni.request可变请求ip地址

文章目录 简介方法一&#xff1a;直接在请求URL中嵌入变量方法二&#xff1a;使用全局变量方法三&#xff1a;使用环境变量方法四&#xff1a;服务端配置方法五&#xff1a;使用配置文件&#xff08;如config.js&#xff09;:总结 简介 在uni-app中&#xff0c;uni.request 用…

深度学习篇---LSTMADF轨迹预测

文章目录 前言LSTM 轨迹预测原理应用在行人轨迹预测方面在自动驾驶车辆的轨迹预测中优点缺点APF 轨迹预测原理应用在船舶运动规划在无人驾驶车辆避障轨迹跟踪优点缺点示例代码前言 本文简单介绍LSTM(长短期记忆网络)和ADF(人工势场法)这两种不同的轨迹预测方法。 LSTM 轨迹…

python实现Web请求与响应

目录 一&#xff1a;什么是Web请求与响应&#xff1f; 1&#xff1a;Web请求 2&#xff1a;Web响应 3&#xff1a;HTTP协议概述 4&#xff1a;常见的HTTP状态码包括&#xff1a; 二&#xff1a;python的requests库 1&#xff1a;安装requests库 2&#xff1a;发送GET请…

Unity使用sherpa-onnx实现说话人识别

网友软绵绵的面包人推荐&#xff0c;模型3dspeaker_speech_eres2net_base_200k_sv_zh-cn_16k-common.onnx的效果比3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx要好 具体代码 using System; using System.Collections.Generic; using System.IO; using Sherpa…

ElasticSearch-集群

本篇文章依据ElasticSearch权威指南进行实操和记录 1&#xff0c;空集群 即不包含任何节点的集群 集群大多数分为两类&#xff0c;主节点和数据节点 主节点 职责&#xff1a;主节点负责管理集群的状态&#xff0c;例如分配分片、添加和删除节点、监控节点故障等。它们不直接…

LG P9844 [ICPC 2021 Nanjing R] Paimon Segment Tree Solution

Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 次修改 ( l , r , v ) (l,r,v) (l,r,v)&#xff1a; 对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r]&#xff0c;令 a i ← a i v a_i\gets a_iv ai​←…

Google Prompt Tuning:文本嵌入优化揭秘

Google Research Prompt Tunin :from_embedded_string 在 Google Research 的 Prompt Tuning 项目代码库 中,from_embedded_string 函数主要用于基于字符串文本初始化提示词的嵌入向量,其调用场景通常与提示词优化或任务适配相关。 1. 核心代码位置 from_embedded_string …

网页 H5 微应用接入钉钉自动登录

ℹ️关于云审批 云审批&#xff08;cloud approve&#xff09; &#xff0c;一款专为小微企业打造&#xff0c;支持多租户的在线审批神器。它简化了申请和审批流程&#xff0c;让您随时随地通过手机或电脑完成请款操作。员工一键提交申请&#xff0c;审批者即时响应&#xff0c…

idea无法识别Maven项目

把.mvn相关都删除了 导致Idea无法识别maven项目 或者 添加导入各个模块 最后把父模块也要导入