零基礎設計模式——創建型模式 - 生成器模式

第二部分:創建型模式 - 生成器模式 (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:最終的文檔。
    • BuilderDocumentBuilder 接口,定義了 buildHeader(), buildBodyParagraph(text), buildListItem(item), buildFooter() 等方法。
    • ConcreteBuilderResumeBuilder (簡歷的頁眉是個人信息,頁腳是聯系方式),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)

在很多現代語言的實現中,BuilderbuildPartX() 方法通常會返回 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.pswp.cn/bicheng/82007.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82007.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82007.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋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項目 或者 添加導入各個模塊 最后把父模塊也要導入