【golang长途旅行第38站】工厂模式
工厂模式
工厂模式是一种非常常用的创建型设计模式,它提供了一种创建对象的方式,而无需向客户端暴露对象的具体创建逻辑。在 Go 语言中,由于其本身没有构造函数和类的概念,工厂模式显得尤为重要和自然。
核心思想
核心思想是:定义一个用于创建对象的接口(或函数),让子类(或具体实现)决定实例化哪一个类(或结构体)。这样,客户端代码只依赖于对象的接口,而不是具体的实现,从而实现了依赖关系的解耦。
为什么在 Go 中使用工厂模式?
-
隐藏创建细节:当对象的创建过程非常复杂(例如需要依赖配置、环境变量、或者需要一系列初始化步骤)时,工厂模式可以将这些复杂性封装起来,为客户端提供一个简洁的创建接口。
-
依赖接口而非实现:客户端代码只需要知道对象提供的接口,而不需要关心其具体的实现类型。这使得代码更灵活,更容易替换和维护。
-
控制实例化:工厂函数可以控制返回的实例。例如,它可以返回一个单例、一个池化后的对象,或者根据条件返回不同子类的实例。
-
命名的好处:Go 的结构体没有构造函数。像 NewMyStruct()这样的工厂函数比 &MyStruct{}更富有表现力,尤其当初始化参数很多时,一个良好的函数名可以清晰地表达创建意图。
工厂模式的三种形式
在 Go 中,工厂模式主要有三种常见的实现形式:
- 简单工厂模式 (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.
}
2. 工厂方法模式 (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.
}
3. 抽象工厂模式 (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.
}