零基礎設計模式——創建型模式 - 抽象工廠模式

第二部分:創建型模式 - 抽象工廠模式 (Abstract Factory Pattern)

我們已經學習了單例模式(保證唯一實例)和工廠方法模式(延遲創建到子類)。現在,我們來探討創建型模式中更為復雜和強大的一個——抽象工廠模式。它處理的是創建“產品族”的問題。

  • 核心思想:提供一個接口,用于創建一系列相關或相互依賴的對象,而無需指定它們具體的類。

抽象工廠模式 (Abstract Factory Pattern)

“提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。”

想象一下,你要裝修房子,可以選擇不同的裝修風格,比如“現代風格”、“中式風格”或“歐式風格”。

  • 每種風格都包含一系列配套的家具:沙發、茶幾、電視柜等。
  • “現代風格”的家具(現代沙發、現代茶幾)是一套,它們之間風格統一。
  • “中式風格”的家具(紅木沙發、雕花茶幾)是另一套,它們也風格統一。

你不會用一個現代風格的沙發去搭配一個中式雕花的茶幾,這樣會顯得不倫不類。抽象工廠模式就是用來確保你得到的是一整套風格協調的產品。

1. 目的 (Intent)

抽象工廠模式的主要目的:

  1. 創建產品族:核心在于創建一系列相關的或相互依賴的對象(稱為一個“產品族”)。例如,一個UI工具包工廠可能創建按鈕、文本框、滾動條等一系列UI組件,這些組件需要有統一的外觀和行為(如Windows風格或macOS風格)。
  2. 客戶端與具體類解耦:客戶端代碼只與抽象的工廠接口和抽象的產品接口打交道,而不需要知道具體是哪個工廠的實現,也不需要知道具體的產品類名。
  3. 保證產品兼容性:由同一個具體工廠創建出來的產品,一定是相互兼容、可以協同工作的。

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

  • 電腦組裝

    • 抽象工廠 (AbstractFactory)ComputerPartsFactory (定義了創建CPU、主板、內存等組件的接口)。
    • 具體工廠 (ConcreteFactory)IntelCompatibleFactory (生產Intel CPU、兼容主板、特定內存條),AMDCompatibleFactory (生產AMD CPU、兼容主板、另一特定內存條)。
    • 抽象產品 (AbstractProduct)CPU, Motherboard, RAM (這些是組件的抽象接口)。
    • 具體產品 (ConcreteProduct)IntelCPU, AMDRyzenCPU, AsusMotherboard, GigabyteMotherboard, KingstonRAM, CorsairRAM
      當你選擇 IntelCompatibleFactory 時,你會得到一套相互兼容的Intel平臺組件。你不會得到一個Intel的CPU卻配一個只支持AMD的主板。
  • 換膚功能 (Skinnable UI)

    • 抽象工廠UIThemeFactory (定義 createButton(), createCheckbox(), createWindow() 等方法)。
    • 具體工廠WindowsThemeFactory (創建Windows風格的按鈕、復選框、窗口),MacThemeFactory (創建Mac風格的按鈕、復選框、窗口),DarkThemeFactory (創建暗黑主題的組件)。
    • 抽象產品Button, Checkbox, Window (UI組件的接口)。
    • 具體產品WindowsButton, MacButton, DarkButton 等。
      用戶選擇一個主題(比如“暗黑主題”),應用就會使用 DarkThemeFactory 來創建所有UI元素,確保界面風格統一。

3. 結構 (Structure)

抽象工廠模式通常包含以下角色:

  1. AbstractFactory (抽象工廠):聲明一個創建抽象產品對象的操作接口集合。通常每個抽象產品對應一個創建方法。
  2. ConcreteFactory (具體工廠):實現 AbstractFactory 接口,負責創建具體產品族中的產品對象。系統可以有多個具體工廠,每個具體工廠創建一個具體的產品族。
  3. AbstractProduct (抽象產品):為一類產品對象聲明一個接口。系統中可以有多個不同的抽象產品,構成產品族。
  4. ConcreteProduct (具體產品):定義一個將被相應的具體工廠創建的產品對象。它實現了 AbstractProduct 接口。
  5. Client (客戶端):僅使用 AbstractFactory 和 AbstractProduct 接口。客戶端不關心具體是哪個工廠、哪個產品,它只知道它需要一個工廠來創建它需要的產品。
    在這里插入圖片描述

4. 適用場景 (When to Use)

  • 一個系統要獨立于它的產品的創建、組合和表示時。即,你希望客戶端代碼與具體產品的創建過程分離。
  • 一個系統要由多個產品系列中的一個來配置時。例如,系統需要支持多種“外觀感覺”(Look and Feel)。
  • 當你要強調一系列相關的產品對象的設計以便進行聯合使用時。
  • 當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。

簡單來說:

  • 需要創建的產品對象有復雜的關聯關系(屬于同一個產品族)
  • 系統需要支持不同系列(族)的產品,并且可以在運行時切換

5. 優缺點 (Pros and Cons)

優點:

  1. 分離接口和實現:客戶端使用抽象接口,與具體的產品實現解耦。
  2. 易于交換產品系列:改變具體工廠即可改變整個產品系列,客戶端代碼無需修改。
  3. 有利于產品的一致性:當一個系列的產品對象被設計成一起工作時,抽象工廠模式能夠保證客戶端始終只使用同一個產品系列中的對象。

缺點:

  1. 難以擴展新的產品種類 (Product Kind):如果要在產品族中增加一個新的產品種類(例如,在UI主題工廠中增加創建 ScrollBar 的方法),那么所有的抽象工廠接口和具體工廠實現都需要修改,這違反了開閉原則。對于這種情況,工廠方法模式可能更合適(每個產品種類一個工廠方法)。
  2. 類的數量會顯著增加:每增加一個產品族,就需要增加一套對應的具體產品類和具體工廠類。

6. 實現方式 (Implementations)

讓我們通過一個跨平臺UI組件的例子來看看抽象工廠模式的實現。假設我們需要為Windows和macOS創建風格一致的按鈕和文本框。

抽象產品 (Button, TextBox)
// ui_elements.go
package ui// Button 按鈕接口 (抽象產品A)
type Button interface {Render()OnClick()
}// TextBox 文本框接口 (抽象產品B)
type TextBox interface {Render()SetText(text string)GetText() string
}
// Button.java
package com.example.ui;// 按鈕接口 (抽象產品A)
public interface Button {void render();void onClick();
}// TextBox.java
package com.example.ui;// 文本框接口 (抽象產品B)
public interface TextBox {void render();void setText(String text);String getText();
}
具體產品 (WindowsButton, WindowsTextBox, MacButton, MacTextBox)
// windows_elements.go
package uiimport "fmt"// WindowsButton Windows風格按鈕 (具體產品A1)
type WindowsButton struct{}func (b *WindowsButton) Render()  { fmt.Println("Rendering a Windows style button.") }
func (b *WindowsButton) OnClick() { fmt.Println("Windows button clicked.") }// WindowsTextBox Windows風格文本框 (具體產品B1)
type WindowsTextBox struct{ text string }func (tb *WindowsTextBox) Render()        { fmt.Println("Rendering a Windows style text box.") }
func (tb *WindowsTextBox) SetText(text string) { tb.text = text }
func (tb *WindowsTextBox) GetText() string   { return tb.text }// mac_elements.go
package uiimport "fmt"// MacButton Mac風格按鈕 (具體產品A2)
type MacButton struct{}func (b *MacButton) Render()  { fmt.Println("Rendering a macOS style button.") }
func (b *MacButton) OnClick() { fmt.Println("macOS button clicked.") }// MacTextBox Mac風格文本框 (具體產品B2)
type MacTextBox struct{ text string }func (tb *MacTextBox) Render()        { fmt.Println("Rendering a macOS style text box.") }
func (tb *MacTextBox) SetText(text string) { tb.text = text }
func (tb *MacTextBox) GetText() string   { return tb.text }
// WindowsButton.java
package com.example.ui.windows;import com.example.ui.Button;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;// Windows風格按鈕 (具體產品A1)
public class WindowsButton implements Button {@Overridepublic void render() {System.out.println("Rendering a Windows style button.");// 實際場景中可能會使用Swing/JavaFX等創建真實UI// JFrame frame = new JFrame("Windows Button");// JButton button = new JButton("Win Button");// button.addActionListener(e -> onClick());// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// frame.setLayout(new FlowLayout());// frame.add(button);// frame.setSize(200, 100);// frame.setVisible(true);}@Overridepublic void onClick() {System.out.println("Windows button clicked.");// JOptionPane.showMessageDialog(null, "Windows Button Clicked!");}
}// WindowsTextBox.java
package com.example.ui.windows;import com.example.ui.TextBox;// Windows風格文本框 (具體產品B1)
public class WindowsTextBox implements TextBox {private String text = "";@Overridepublic void render() {System.out.println("Rendering a Windows style text box: [" + text + "]");}@Overridepublic void setText(String text) { this.text = text; }@Overridepublic String getText() { return this.text; }
}// MacButton.java
package com.example.ui.mac;import com.example.ui.Button;// Mac風格按鈕 (具體產品A2)
public class MacButton implements Button {@Overridepublic void render() {System.out.println("Rendering a macOS style button.");}@Overridepublic void onClick() {System.out.println("macOS button clicked.");}
}// MacTextBox.java
package com.example.ui.mac;import com.example.ui.TextBox;// Mac風格文本框 (具體產品B2)
public class MacTextBox implements TextBox {private String text = "";@Overridepublic void render() {System.out.println("Rendering a macOS style text box: (" + text + ")");}@Overridepublic void setText(String text) { this.text = text; }@Overridepublic String getText() { return this.text; }
}
抽象工廠 (GUIFactory)
// gui_factory.go
package ui// GUIFactory 抽象UI工廠 (抽象工廠)
type GUIFactory interface {CreateButton() ButtonCreateTextBox() TextBox
}
// GUIFactory.java
package com.example.ui;// 抽象UI工廠 (抽象工廠)
public interface GUIFactory {Button createButton();TextBox createTextBox();
}
具體工廠 (WindowsFactory, MacFactory)
// windows_factory.go
package ui// WindowsFactory Windows UI工廠 (具體工廠1)
type WindowsFactory struct{}func (wf *WindowsFactory) CreateButton() Button {return &WindowsButton{}
}
func (wf *WindowsFactory) CreateTextBox() TextBox {return &WindowsTextBox{}
}// mac_factory.go
package ui// MacFactory Mac UI工廠 (具體工廠2)
type MacFactory struct{}func (mf *MacFactory) CreateButton() Button {return &MacButton{}
}
func (mf *MacFactory) CreateTextBox() TextBox {return &MacTextBox{}
}
// WindowsFactory.java
package com.example.ui.windows;import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;// Windows UI工廠 (具體工廠1)
public class WindowsFactory implements GUIFactory {@Overridepublic Button createButton() {System.out.println("WindowsFactory: Creating WindowsButton");return new WindowsButton();}@Overridepublic TextBox createTextBox() {System.out.println("WindowsFactory: Creating WindowsTextBox");return new WindowsTextBox();}
}// MacFactory.java
package com.example.ui.mac;import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;// Mac UI工廠 (具體工廠2)
public class MacFactory implements GUIFactory {@Overridepublic Button createButton() {System.out.println("MacFactory: Creating MacButton");return new MacButton();}@Overridepublic TextBox createTextBox() {System.out.println("MacFactory: Creating MacTextBox");return new MacTextBox();}
}
客戶端使用 (Application)
// main.go (示例用法)
/*
package mainimport ("fmt""./ui" // 假設 ui 包在當前目錄下"runtime"
)// Application 客戶端,它不知道具體的工廠和產品類
type Application struct {factory GUIFactorybutton  ButtontextBox TextBox
}func NewApplication(factory ui.GUIFactory) *Application {app := &Application{factory: factory}app.button = factory.CreateButton()app.textBox = factory.CreateTextBox()return app
}func (app *Application) Run() {app.button.Render()app.button.OnClick()app.textBox.SetText("Hello Abstract Factory!")app.textBox.Render()fmt.Println("Text from box:", app.textBox.GetText())
}func main() {var factory ui.GUIFactory// 根據操作系統選擇不同的工廠os := runtime.GOOSfmt.Println("Operating System:", os)if os == "windows" {factory = &ui.WindowsFactory{}} else if os == "darwin" { // darwin is macOSfactory = &ui.MacFactory{}} else {fmt.Println("Unsupported OS, defaulting to Windows style.")factory = &ui.WindowsFactory{} // 默認或提供一個通用工廠}app := NewApplication(factory)app.Run()
}
*/
// Application.java (客戶端)
package com.example;import com.example.ui.Button;
import com.example.ui.GUIFactory;
import com.example.ui.TextBox;
import com.example.ui.mac.MacFactory;
import com.example.ui.windows.WindowsFactory;public class Application {private Button button;private TextBox textBox;public Application(GUIFactory factory) {System.out.println("Client: Configuring application with a UI factory.");button = factory.createButton();textBox = factory.createTextBox();}public void run() {System.out.println("\nClient: Running the application UI...");button.render();button.onClick();textBox.setText("Hello Abstract Factory!");textBox.render();System.out.println("Text from box: " + textBox.getText() + "\n");}// Main.java (示例用法)/*public static void main(String[] args) {GUIFactory factory;Application app;String osName = System.getProperty("os.name").toLowerCase();System.out.println("Operating System: " + osName);if (osName.contains("win")) {factory = new WindowsFactory();} else if (osName.contains("mac")) {factory = new MacFactory();} else {System.out.println("Unsupported OS, defaulting to Windows style.");factory = new WindowsFactory(); // Default factory}app = new Application(factory);app.run();// 假設我們現在想切換到Mac主題 (如果當前不是Mac)if (!osName.contains("mac")) {System.out.println("\n--- Switching to Mac Theme for demonstration ---");factory = new MacFactory();app = new Application(factory);app.run();}}*/
}

7. 與工廠方法模式的區別

抽象工廠模式和工廠方法模式是初學者容易混淆的兩個模式。

  • 工廠方法模式 (Factory Method)

    • 關注點:創建單個產品對象
    • 結構:一個抽象工廠接口(通常只有一個創建方法 factoryMethod()),多個具體工廠實現它來創建不同的具體產品。
    • 目的:延遲產品的實例化到子類。
    • 解決問題:如何創建一個對象,但讓子類決定具體創建哪個對象。
  • 抽象工廠模式 (Abstract Factory)

    • 關注點:創建一系列相關的產品對象(一個產品族)
    • 結構:一個抽象工廠接口(包含多個創建不同種類產品的抽象方法,如 createProductA(), createProductB()),多個具體工廠實現它來創建屬于同一個產品族的不同具體產品。
    • 目的:提供一個接口,用于創建一系列相關或相互依賴的對象,而無需指定它們具體的類。
    • 解決問題:如何創建一組相互關聯/依賴的對象,并保證它們之間是兼容的。

簡單來說

  • 如果你只需要創建一種產品,但希望由子類決定具體創建哪種類型,用工廠方法
  • 如果你需要創建多種產品,這些產品需要配套使用(屬于一個系列/族),并且希望客戶端與具體產品解耦,用抽象工廠

實際上,抽象工廠模式的實現中,每個具體工廠內部的創建方法(如 createButton())通常可以使用工廠方法模式來實現,或者直接 new 具體產品。

8. 總結

抽象工廠模式是創建型模式中功能最強大但也相對復雜的模式之一。它通過提供一個抽象接口來創建一系列相關的產品對象(產品族),使得客戶端代碼可以獨立于具體的產品實現。這對于需要支持多種產品系列(例如不同的UI主題、不同的數據庫實現)并且希望在它們之間輕松切換的系統非常有用。

記住它的核心:創建產品家族,保證兼容性

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84061.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84061.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84061.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【通用智能體】Serper API 詳解:搜索引擎數據獲取的核心工具

Serper API 詳解:搜索引擎數據獲取的核心工具 一、Serper API 的定義與核心功能二、技術架構與核心優勢2.1 技術實現原理2.2 對比傳統方案的突破性優勢 三、典型應用場景與代碼示例3.1 SEO 監控系統3.2 競品廣告分析 四、使用成本與配額策略五、開發者注意事項六、替…

Flask-SQLAlchemy核心概念:模型類與數據庫表、類屬性與表字段、外鍵與關系映射

前置閱讀,關于Flask-SQLAlchemy支持哪些數據庫及基本配置,鏈接:Flask-SQLAlchemy_數據庫配置 摘要 本文以一段典型的 SQLAlchemy 代碼示例為引入,闡述以下核心概念: 模型類(Model Class) ? 數…

野火魯班貓(arrch64架構debian)從零實現用MobileFaceNet算法進行實時人臉識別(四)安裝RKNN Toolkit2

RKNN Toolkit2是用來將onnx模型轉成rknn專用模型,并可通過RKNN Toolkit Lite2或者RKNPU調用NPU進行加速計算的工具。 一開始我安裝很多次都無法成功安裝。后來跟售后技術對接,必須是PC平臺的Linux環境才可以。我的電腦是windows,所以我需要用…

基于深度學習的工件檢測系統設計與實現

在工業自動化領域,工件檢測一直是提高生產效率和產品質量的關鍵環節。傳統的人工檢測方法不僅效率低下,而且容易受到主觀因素的影響,導致誤判率較高。隨著深度學習技術的飛速發展,基于圖像識別的自動檢測系統逐漸成為研究熱點。今…

CyberSecAsia專訪CertiK首席安全官:區塊鏈行業亟需“安全優先”開發范式

近日,權威網絡安全媒體CyberSecAsia發布了對CertiK首席安全官Wang Tielei博士的專訪,雙方圍繞企業在進軍區塊鏈領域時所面臨的關鍵安全風險與防御策略展開深入探討。 Wang博士在采訪中指出,跨鏈橋攻擊、智能合約漏洞以及私鑰管理不當&#x…

Google C++ Style Guide 谷歌 C++編碼風格指南,深入理解華為與谷歌的編程規范——C和C++實踐指南

Google C 編程風格指南 Release Apr 07, 2017 0. ?享 ?? 4.45 ??? Benjy Weinberger, Craig Silverstein, Gregory Eitzmann, Mark Mentovai, Tashana Landray ?? YuleFox, Yang.Y, acgtyrant, lilinsanity 亯??享 ? Google Style Guide ? Google 開源…

當科技邂逅浪漫:在Codigger的世界里,遇見“愛”

520,一個充滿愛意的日子,人們用各種方式表達對彼此的深情。而在科技的世界里,我們也正經歷著一場特別的邂逅——Codigger,一個分布式操作系統的誕生,正在以它獨特的方式,重新定義我們與技術的關系。 Codigg…

嵌入式學習筆記 - Void類型的指針

void指針的基本概念和特性 void指針是一種特殊的指針類型,稱為“無類型指針”或“通用指針”。它的主要特點是: ?通用性?:void指針可以指向任何類型的數據,這使得它在處理不確定數據類型時非常有用。 ?靈活性?:由…

【綜述】視頻目標分割VOS

相關連接 更新中....... 1、Associating Objects with Transformers for Video Object Segmentation:論文詳解、AOT源碼解析 2、Rethinking Space-Time Networks with Improved Memory Coverage for Efficient Video Object Segmentation 3、Recurrent Dynamic Embe…

001 嵌入式軟件開發工程師實習篇面試——首戰總結

2025年5月17日人生中第一次面試 緊張是藏不住的。但是不應該的。 目錄 0.準備一份合適的自我介紹 1.結構體內存對齊問題 2.變量在內存中的存儲模式 3.嵌入式中程序框架有哪些 4.程序代碼設計要遵循什原則 5.版本號書寫 6.單片機最小系統板有哪些組成 必須: 非必須:…

SIL2/PLd 認證 Inxpect毫米波安全雷達:3D 掃描 + 微小運動檢測守護工業安全

Inxpect 成立于意大利,專注工業安全技術。自成立起,便致力于借助先進雷達技術提升工業自動化安全標準,解決傳統安全設備在復雜環境中的局限,推出獲 SIL2/PLd 和 UL 認證的安全雷達產品。 Inxpect 的雷達傳感器技術優勢明顯。相較于…

Python數據可視化再探——Matplotlib模塊 之一

目錄 第一章 Matplotlib 模塊教學內容?——基礎圖形繪制 一、Pyplot 子庫介紹? 1. 功能概述? 2. 常用函數? 二、繪制基本圖形? 1. 柱狀圖? 2. 條形圖? 3. 折線圖? 4. 散點圖? 5. 面積圖? 6. 餅狀圖? 7. 圓環圖? ?編輯 三、繪圖知識點詳解? 1. 繪圖…

智慧在線判題OJ系統項目總體,包含功能開發思路,內部中間件,已經部分知識點

目錄 回顧一下xml文件怎么寫 哪個地方使用了哪個技術 MyBatis-Plus-oj的表結構設計, 管理員登錄功能 Swagger Apifox?編輯 BCrypt 日志框架引入(slf4jlogback) nacos Swagger無法被所有微服務獲取到修改的原因 身份認證三種方式: JWT(Json Web Json,一…

使用Spring Boot和Spring Security構建安全的RESTful API

使用Spring Boot和Spring Security構建安全的RESTful API 引言 在現代Web應用開發中,安全性是至關重要的。Spring Boot和Spring Security是Java生態中廣泛使用的框架,它們提供了強大的工具來保護RESTful API。本文將介紹如何結合Spring Boot和Spring S…

虛幻引擎5-Unreal Engine筆記之`GameMode`、`關卡(Level)` 和 `關卡藍圖(Level Blueprint)`的關系

虛幻引擎5-Unreal Engine筆記之GameMode、關卡(Level) 和 關卡藍圖(Level Blueprint)的關系 code review! 參考筆記: 1.虛幻引擎5-Unreal Engine筆記之GameMode、關卡(Level) 和 關卡藍圖&…

Java+Selenium+快代理實現高效爬蟲

目錄 一、前言二、Selenium簡介三、環境準備四、代碼實現4.1 創建WebDriver工廠類4.2 創建爬蟲主類4.3 配置代理的注意事項 六、總結與展望 一、前言 在Web爬蟲技術中,Selenium作為一款強大的瀏覽器自動化工具,能夠模擬真實用戶操作,有效應對…

SpringBoot配置文件的合并

需求:想分類將mysql數據庫的配置放在一個文件,redis的配置放在另外一個文件 就不去引入mysql和redis了,看能否得到值就行了 測試結果 model的包放錯了 應該移動到demo里 能否用yml或者yaml呢 這里注意yml的寫法 測試結果也是可以的 注意如果主配置文件是yml或者yaml的話

深入理解 BFC:網頁布局的關鍵機制

在前端開發的世界里,網頁布局是一項至關重要的任務。而在眾多布局相關的概念中,BFC(Block Formatting Context,塊級格式化上下文)扮演著極為關鍵的角色。今天,就讓我們深入剖析 BFC 的方方面面。 一、BFC …

04-Web后端基礎(基礎知識)

而像HTML、CSS、JS 以及圖片、音頻、視頻等這些資源,我們都稱為靜態資源。 所謂靜態資源,就是指在服務器上存儲的不會改變的數據,通常不會根據用戶的請求而變化。 那與靜態資源對應的還有一類資源,就是動態資源。那所謂動態資源&…

Vue3 Element Plus el-table-column Sortable 排序失效

問題描述&#xff1a; vue3中 element plus 中 el-table 的 el-table-column使用了插槽后&#xff0c;為什么sortable不起效果&#xff0c;不能點擊排序 <el-table-columnlabel"記賬日期"width"110"fixed"left"header-align"left"…