【golang长途旅行第38站】工厂模式

工厂模式

工厂模式是一种非常常用的创建型设计模式,它提供了一种创建对象的方式,而无需向客户端暴露对象的具体创建逻辑。在 Go 语言中,由于其本身没有构造函数和类的概念,工厂模式显得尤为重要和自然。

核心思想

核心思想是:​定义一个用于创建对象的接口(或函数),让子类(或具体实现)决定实例化哪一个类(或结构体)​。这样,客户端代码只依赖于对象的接口,而不是具体的实现,从而实现了依赖关系的解耦。

为什么在 Go 中使用工厂模式?

  1. ​隐藏创建细节​:当对象的创建过程非常复杂(例如需要依赖配置、环境变量、或者需要一系列初始化步骤)时,工厂模式可以将这些复杂性封装起来,为客户端提供一个简洁的创建接口。

  2. ​依赖接口而非实现​:客户端代码只需要知道对象提供的接口,而不需要关心其具体的实现类型。这使得代码更灵活,更容易替换和维护。

  3. ​控制实例化​:工厂函数可以控制返回的实例。例如,它可以返回一个单例、一个池化后的对象,或者根据条件返回不同子类的实例。

  4. ​命名的好处​:Go 的结构体没有构造函数。像 NewMyStruct()这样的工厂函数比 &MyStruct{}更富有表现力,尤其当初始化参数很多时,一个良好的函数名可以清晰地表达创建意图。

工厂模式的三种形式

在 Go 中,工厂模式主要有三种常见的实现形式:

  1. 简单工厂模式 (Simple Factory)
    这是最基础的形式,通常就是一个简单的函数,根据传入的参数返回不同的对象实例。
    package main

import "fmt"

// 1. 定义接口

type Logger interface {

Log(message string)

}

// 2. 定义具体实现:文件日志

type FileLogger struct{}

func (f *FileLogger) Log(message string) {

fmt.Printf("Logging to file: %s\n", message)

}

// 3. 定义具体实现:标准输出日志

type StdoutLogger struct{}

func (s *StdoutLogger) Log(message string) {

fmt.Printf("Logging to stdout: %s\n", message)

}

// 4. 工厂函数:核心所在

func NewLogger(logType string) Logger {

switch logType {

case "file":

return &FileLogger{}

case "stdout":

fallthrough // 落到 default

default:

return &StdoutLogger{}

}

}

// 客户端代码

func main() {

// 客户端只需要知道 Logger 接口和工厂函数

// 而不需要知道 FileLogger 或 StdoutLogger 的具体存在

var logger Logger

复制代码
logger = NewLogger("file")
logger.Log("This goes to a file.") // Output: Logging to file: This goes to a file.

logger = NewLogger("stdout")
logger.Log("This goes to stdout.") // Output: Logging to stdout: This goes to stdout.

}

  1. 工厂方法模式 (Factory Method)

它为每一类产品提供一个独立的工厂函数,从而避免了在简单工厂中使用条件判断。

​示例:创建不同的数据库连接

package main

import "fmt"

// 数据库连接接口

type DatabaseConnector interface {

Connect()

}

// 具体实现:MySQL

type MySQLConnector struct{}

func (m *MySQLConnector) Connect() {

fmt.Println("Connected to MySQL database.")

}

// 工厂函数 for MySQL

func NewMySQLConnector() DatabaseConnector {

return &MySQLConnector{}

}

// 具体实现:PostgreSQL

type PostgreSQLConnector struct{}

func (p *PostgreSQLConnector) Connect() {

fmt.Println("Connected to PostgreSQL database.")

}

// 工厂函数 for PostgreSQL

func NewPostgreSQLConnector() DatabaseConnector {

return &PostgreSQLConnector{}

}

// 客户端代码

func main() {

// 根据需要调用不同的工厂函数

var dbConn DatabaseConnector

复制代码
dbConn = NewMySQLConnector()
dbConn.Connect() // Output: Connected to MySQL database.

dbConn = NewPostgreSQLConnector()
dbConn.Connect() // Output: Connected to PostgreSQL database.

}

  1. 抽象工厂模式 (Abstract Factory)

这是最复杂的形式,用于创建一系列相关或依赖的对象家族,而不需要指定它们具体的类。

​示例:创建跨平台的 UI 组件

package main

import "fmt"

// ------ 抽象产品接口 ------

type Button interface {

Render()

}

type TextBox interface {

Display()

}

// ------ 抽象工厂接口 ------

type UIFactory interface {

CreateButton() Button

CreateTextBox() TextBox

}

// ------ 具体产品 & 具体工厂 for Windows ------

type WinButton struct{}

func (w WinButton) Render() {

fmt.Println("Rendering a Windows-style button.")

}

type WinTextBox struct{}

func (w WinTextBox) Display() {

fmt.Println("Displaying a Windows-style textbox.")

}

type WinFactory struct{}

func (w WinFactory) CreateButton() Button {

return WinButton{}

}

func (w WinFactory) CreateTextBox() TextBox {

return WinTextBox{}

}

// ------ 具体产品 & 具体工厂 for macOS ------

type MacButton struct{}

func (m MacButton) Render() {

fmt.Println("Rendering a macOS-style button.")

}

type MacTextBox struct{}

func (m MacTextBox) Display() {

fmt.Println("Displaying a macOS-style textbox.")

}

type MacFactory struct{}

func (m MacFactory) CreateButton() Button {

return MacButton{}

}

func (m MacFactory) CreateTextBox() TextBox {

return MacTextBox{}

}

// ------ 客户端代码 ------

// 获取一个工厂,这个工厂能创建一整套风格一致的UI组件

func GetUIFactory(platform string) UIFactory {

switch platform {

case "windows":

return WinFactory{}

case "mac":

return MacFactory{}

default:

panic("unsupported platform")

}

}

func main() {

// 根据运行环境决定使用哪个工厂

factory := GetUIFactory("mac") // 切换为 "windows" 试试

复制代码
// 用同一个工厂创建一套协调的UI组件
button := factory.CreateButton()
textbox := factory.CreateTextBox()

button.Render()   // Output: Rendering a macOS-style button.
textbox.Display() // Output: Displaying a macOS-style textbox.

}

相关推荐
come112348 小时前
Go 和云原生 的现状和发展前景
开发语言·云原生·golang
专家大圣8 小时前
Bililive-go+cpolar:跨平台直播录制的远程管理方案
开发语言·网络·后端·golang·内网穿透·设计工具
Eiceblue8 小时前
Python 将 HTML 转换为纯文本 TXT (HTML 文本提取)
开发语言·vscode·python·html
Cache技术分享8 小时前
202. Java 异常 - throw 语句的使用
前端·后端
程序员小假8 小时前
我们来说一说 ThreadLocal 内存泄漏
java·后端
张人玉8 小时前
C# TCP - 串口转发
开发语言·tcp/ip·c#
moisture8 小时前
SGLang 混合并行
后端·llm
我不是混子8 小时前
聊聊Spring事件机制
java·后端
Mintopia9 小时前
🚪 当 Next.js 中间件穿上保安制服:请求拦截与权限控制的底层奇幻之旅
前端·后端·next.js
苦逼大学生被编程薄纱9 小时前
C++ 容器学习系列|vector 核心知识全解析,铺垫下一期模拟实现
开发语言·c++·学习