文章目录
- 工厂模式
- 工厂模式 - 线程安全
- 原型模式
- 建造者模式
工厂模式
工厂模式是一种创建型设计模式,旨在提供一种封装对象创建过程的方式,使得客户端代码不必直接依赖于具体的对象创建细节。在 Go 语言中,工厂模式通常通过接口和结构体组合来实现。
在工厂模式中,通常会定义一个工厂接口,该接口包含一个用于创建产品的方法。然后针对每种产品,都实现一个具体的工厂类,用于创建该产品的实例。客户端代码只需要通过工厂接口来创建对象,而无需关心具体的创建过程。
- 简单工厂模式(Simple Factory Pattern): 简单工厂模式通过一个工厂类来负责创建所有产品的实例。客户端通过向工厂传递参数来指定要创建的产品类型。这种模式简单易懂,但缺点是不够灵活,当需要添加新的产品类型时,需要修改工厂类。
- 工厂方法模式(Factory Method Pattern): 工厂方法模式将具体产品的创建延迟到具体的工厂类中。每个产品都有对应的工厂类,客户端通过调用工厂类的方法来创建产品。这种模式可以实现产品与工厂的解耦,支持扩展,但可能会导致类的数量增加。
- 抽象工厂模式(Abstract Factory Pattern): 抽象工厂模式提供一个创建一系列相关或依赖对象的接口,而无需指定它们具体的类。每个工厂类都可以创建一组相关的产品,从而使得产品之间的兼容性更好。这种模式适用于创建复杂的对象族,但增加新的产品类型可能需要修改接口及所有的实现。
-
场景
- 数据库驱动: 当你需要连接不同类型的数据库时,可以使用工厂模式创建数据库连接对象。根据配置或者用户需求,工厂可以返回不同类型的数据库连接实例。
- 日志记录器: 在应用中需要记录日志,但你可能会使用不同的日志记录库,比如logrus、zap等。通过工厂模式,你可以在应用启动时确定使用哪个日志记录器,并在整个应用中使用相同的接口来记录日志。
- 网络请求客户端: 当你需要与不同的网络服务进行通信时,可以使用工厂模式创建网络请求客户端。根据服务的不同特点或需求,工厂可以返回相应的客户端实例,如HTTP客户端、gRPC客户端等。
- 加密算法: 在需要加密或者解密数据时,可能会使用不同的加密算法,比如AES、RSA等。通过工厂模式,你可以根据需要创建相应的加密器实例。
- 插件系统: 如果你正在开发一个支持插件的应用程序,工厂模式可以用来动态加载并实例化插件。根据插件类型或其他条件,工厂可以创建不同的插件实例。
-
优点
- 解耦合: 工厂模式可以将对象的创建逻辑与使用对象的代码分离,从而降低了它们之间的耦合度。这样一来,如果需要更改对象的创建方式,只需修改工厂而不影响使用该对象的代码。
- 灵活性: 工厂模式可以根据需要动态地创建不同类型的对象实例,使得系统更加灵活。通过工厂方法,可以在运行时根据条件选择合适的对象类型。
- 代码复用: 工厂模式可以避免在代码中重复创建对象的逻辑,提高了代码的重用性。通过工厂方法统一对象的创建,可以减少重复的代码片段。
- 易于扩展: 当需要添加新的产品类时,只需扩展工厂方法即可,无需修改客户端代码。这使得系统更容易扩展和维护。
-
缺点
- 类爆炸: 如果系统中存在大量的产品类,可能会导致工厂类变得庞大且难以维护。这种情况下,可能需要使用其他模式来管理产品类的创建。
- 违反开闭原则: 如果每次添加新的产品类都需要修改工厂方法,那么工厂模式就可能违反了开闭原则。开闭原则要求系统对扩展开放,对修改关闭,但是频繁修改工厂方法可能导致对修改的开放。
- 过度设计: 在简单场景下使用工厂模式可能会显得过度设计,增加了代码的复杂性。在不必要的情况下过度使用设计模式可能会带来不必要的开销。
-
简单工厂模式示例
package mainimport "fmt"// 定义接口:水果
type Fruit interface {Eat() string // 定义吃水果的方法
}// 定义具体产品类:苹果
type Apple struct{}// 实现苹果的 Eat 方法
func (a *Apple) Eat() string {return "吃苹果"
}// 定义具体产品类:香蕉
type Banana struct{}// 实现香蕉的 Eat 方法
func (b *Banana) Eat() string {return "吃香蕉"
}// 定义水果工厂类
type FruitFactory struct{}// 创建水果的方法
func (ff *FruitFactory) CreateFruit(fruitType string) Fruit {switch fruitType {case "apple":return &Apple{} // 返回苹果实例case "banana":return &Banana{} // 返回香蕉实例default:panic("不支持的水果类型")}
}func main() {// 创建水果工厂factory := &FruitFactory{}// 使用工厂创建苹果对象apple := factory.CreateFruit("apple")fmt.Println(apple.Eat()) // 输出:吃苹果// 使用工厂创建香蕉对象banana := factory.CreateFruit("banana")fmt.Println(banana.Eat()) // 输出:吃香蕉
}
- 输出结果
吃苹果
吃香蕉
- 工厂方法模式示例
package mainimport "fmt"// 定义接口:运算器
type Operator interface {Operate(int, int) int // 定义运算方法
}// 定义具体产品类:加法运算器
type AddOperator struct{}// 实现加法运算器的 Operate 方法
func (a *AddOperator) Operate(num1, num2 int) int {return num1 + num2
}// 定义具体产品类:减法运算器
type SubtractOperator struct{}// 实现减法运算器的 Operate 方法
func (s *SubtractOperator) Operate(num1, num2 int) int {return num1 - num2
}// 定义工厂接口
type OperatorFactory interface {Create() Operator // 创建运算器的方法
}// 加法运算器工厂
type AddFactory struct{}// 实现加法运算器工厂的 Create 方法
func (af *AddFactory) Create() Operator {return &AddOperator{}
}// 减法运算器工厂
type SubtractFactory struct{}// 实现减法运算器工厂的 Create 方法
func (sf *SubtractFactory) Create() Operator {return &SubtractOperator{}
}func main() {// 创建加法运算器工厂addFactory := &AddFactory{}// 使用加法运算器工厂创建加法运算器add := addFactory.Create()// 调用加法运算器的 Operate 方法fmt.Println("3 + 2 =", add.Operate(3, 2)) // 输出:3 + 2 = 5// 创建减法运算器工厂subtractFactory := &SubtractFactory{}// 使用减法运算器工厂创建减法运算器subtract := subtractFactory.Create()// 调用减法运算器的 Operate 方法fmt.Println("3 - 2 =", subtract.Operate(3, 2)) // 输出:3 - 2 = 1
}
- 输出结果
3 + 2 = 5
3 - 2 = 1
- 抽象工厂模式示例
package mainimport "fmt"// 定义接口:形状
type Shape interface {Draw() string // 绘制形状的方法
}// 定义具体产品类:圆形
type Circle struct{}// 实现圆形的 Draw 方法
func (c *Circle) Draw() string {return "画一个圆形"
}// 定义具体产品类:矩形
type Rectangle struct{}// 实现矩形的 Draw 方法
func (r *Rectangle) Draw() string {return "画一个矩形"
}// 定义接口:颜色
type Color interface {Fill() string // 填充颜色的方法
}// 定义具体产品类:红色
type Red struct{}// 实现红色的 Fill 方法
func (r *Red) Fill() string {return "填充红色"
}// 定义具体产品类:蓝色
type Blue struct{}// 实现蓝色的 Fill 方法
func (b *Blue) Fill() string {return "填充蓝色"
}// 定义抽象工厂接口
type AbstractFactory interface {CreateShape() Shape // 创建形状的方法CreateColor() Color // 创建颜色的方法
}// 具体工厂类:形状为圆形,颜色为红色的工厂
type RedCircleFactory struct{}// 实现创建形状的方法
func (rcf *RedCircleFactory) CreateShape() Shape {return &Circle{}
}// 实现创建颜色的方法
func (rcf *RedCircleFactory) CreateColor() Color {return &Red{}
}// 具体工厂类:形状为矩形,颜色为蓝色的工厂
type BlueRectangleFactory struct{}// 实现创建形状的方法
func (brf *BlueRectangleFactory) CreateShape() Shape {return &Rectangle{}
}// 实现创建颜色的方法
func (brf *BlueRectangleFactory) CreateColor() Color {return &Blue{}
}func main() {// 创建形状为圆形,颜色为红色的工厂redCircleFactory := &RedCircleFactory{}// 使用工厂创建形状shape1 := redCircleFactory.CreateShape()// 使用工厂创建颜色color1 := redCircleFactory.CreateColor()// 调用形状和颜色的方法fmt.Println(shape1.Draw()) // 输出:画一个圆形fmt.Println(color1.Fill()) // 输出:填充红色// 创建形状为矩形,颜色为蓝色的工厂blueRectangleFactory := &BlueRectangleFactory{}// 使用工厂创建形状shape2 := blueRectangleFactory.CreateShape()// 使用工厂创建颜色color2 := blueRectangleFactory.CreateColor()// 调用形状和颜色的方法fmt.Println(shape2.Draw()) // 输出:画一个矩形fmt.Println(color2.Fill()) // 输出:填充蓝色
}
- 输出结果
画一个圆形
填充红色
画一个矩形
填充蓝色
工厂模式 - 线程安全
- 使用互斥锁(Mutex):在工厂函数中使用互斥锁来保护共享资源,确保每次只有一个goroutine能够访问该资源。
type Factory struct {mu sync.Mutex
}func (f *Factory) CreateObject() Object {f.mu.Lock()defer f.mu.Unlock()// 创建对象的逻辑
}
- 使用读写锁(RWMutex):如果创建对象的操作不涉及修改共享状态,可以使用读写锁来允许多个goroutine同时读取对象,但只有一个goroutine能够写入对象。
type Factory struct {mu sync.RWMutex
}func (f *Factory) CreateObject() Object {f.mu.Lock()defer f.mu.Unlock()// 创建对象的逻辑
}
- 使用通道(Channel):利用无缓冲通道的阻塞特性,确保只有一个goroutine能够执行工厂函数。
type Factory struct {ch chan struct{}
}func NewFactory() *Factory {return &Factory{ch: make(chan struct{}, 1)}
}func (f *Factory) CreateObject() Object {f.ch <- struct{}{} // 发送信号,阻塞其他goroutinedefer func() { <-f.ch }() // 接收信号,释放通道// 创建对象的逻辑
}
- 使用 sync.Once:如果只需要在第一次调用时初始化对象,并且不需要传递任何参数,可以使用sync.Once确保初始化逻辑只执行一次。
type Factory struct {once sync.Onceobj Object
}func (f *Factory) CreateObject() Object {f.once.Do(func() {// 初始化对象的逻辑f.obj = initializeObject()})return f.obj
}
原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,其主要思想是通过复制现有对象来创建新对象,而不是通过实例化新对象的方式。在原型模式中,我们首先创建一个原型对象(即原型),然后通过复制这个原型来创建新的对象实例。
在Go中,原型模式通常通过深拷贝(deep copy)或者浅拷贝(shallow copy)来实现对象的复制。深拷贝会复制对象的所有字段和嵌套的对象,而浅拷贝只会复制对象本身,但不会复制对象中的嵌套对象。
-
场景
- 对象初始化复杂:当对象的初始化过程比较复杂或者耗时时,可以使用原型模式来避免重复的初始化操作。通过复制现有对象来创建新对象,可以节省初始化时间,提高系统性能。
- 动态创建对象:当需要根据运行时的条件动态创建对象实例时,原型模式非常有用。通过原型模式,可以动态地增加新的原型对象,并根据需要进行复制,从而创建新的对象实例。
- 对象创建过程无法预知:当对象的创建过程无法提前预知或者需要根据不同情况进行调整时,原型模式可以提供一种灵活的创建方式。通过复制现有对象来创建新对象,可以根据需要动态调整对象的属性和行为。
- 保护对象的初始化状态:有些对象在初始化之后需要保持某种状态,但又需要不断地创建新的实例。通过原型模式,可以保护对象的初始化状态,并在需要时进行复制,从而创建新的实例而不影响原始对象的状态。
-
优点
- 减少对象创建的开销:通过复制现有对象来创建新对象,可以避免重复的初始化过程,减少对象创建的开销。
- 简化对象的创建过程:原型模式将对象的创建与使用分离开来,使得创建新对象的过程变得简单。
- 动态增加新的对象:原型模式允许动态地增加新的原型对象,从而使得系统更加灵活。
-
缺点
- 对象的复制可能会比较复杂:如果对象中包含了其他对象的引用或者非导出字段,那么对象的复制可能会比较复杂,需要额外的处理。
- 可能会影响性能:如果对象的复制过程比较耗时,可能会影响系统的性能。
-
浅拷贝示例
package mainimport "fmt"// 定义接口:原型
type Prototype interface {Clone() Prototype // 克隆方法
}// 具体产品类:狗
type Dog struct {Name stringAge int
}// 实现原型的 Clone 方法
func (d *Dog) Clone() Prototype {return &Dog{Name: d.Name, Age: d.Age}
}func main() {// 创建原型对象originalDog := &Dog{Name: "旺财", Age: 3}// 克隆原型对象clonedDog := originalDog.Clone().(*Dog)// 修改克隆对象的属性clonedDog.Name = "小白"clonedDog.Age = 2// 输出原型对象信息fmt.Println("原型对象:", originalDog.Name, originalDog.Age)// 输出克隆对象信息fmt.Println("克隆对象:", clonedDog.Name, clonedDog.Age)
}
- 输出结果
型对象: 旺财 3
克隆对象: 小白 2
- 深拷贝示例
package mainimport ("bytes""encoding/gob""fmt"
)// Address 是地址结构体
type Address struct {Street stringCity stringZipCode stringCountry stringBuilding string
}// Person 是人员结构体
type Person struct {Name stringAge intAddress *Address
}// CloneAddress 深拷贝地址结构体
func CloneAddress(addr *Address) *Address {var buf bytes.Bufferenc := gob.NewEncoder(&buf)_ = enc.Encode(addr)dec := gob.NewDecoder(&buf)clone := new(Address)_ = dec.Decode(clone)return clone
}// ClonePerson 深拷贝人员结构体
func (p *Person) Clone() *Person {return &Person{Name: p.Name,Age: p.Age,Address: CloneAddress(p.Address),}
}func main() {// 创建原型对象original := &Person{Name: "Alice",Age: 30,Address: &Address{Street: "123 Main St",City: "Anytown",ZipCode: "12345",Country: "USA",Building: "Apt 101",},}// 创建原型对象的深拷贝副本clone := original.Clone()// 修改原型对象的地址信息original.Address.City = "New City"original.Address.Building = "Apt 202"// 打印原型对象和副本对象的地址信息fmt.Println("Original Address:", original.Address)fmt.Println("Cloned Address:", clone.Address)
}
- 输出结果
Original Address: &{123 Main St New City 12345 USA Apt 202}
Cloned Address: &{123 Main St Anytown 12345 USA Apt 101}
建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。它将对象的构建过程拆分成多个步骤,然后使用相同的构建过程来构建不同的表示。
- 产品(Product):表示被构建的复杂对象,包含多个部件。
- 建造者(Builder):定义了构建产品的抽象接口,包含了构建产品各个部件的方法。
- 具体建造者(Concrete Builder):实现了建造者接口,负责具体产品的构建过程。通常会有多个具体建造者类,用于构建不同的产品表示。
- 指挥者(Director):负责使用建造者对象构建复杂对象,它将客户端与构建过程解耦,通常不直接与产品类交互。
-
场景
- 复杂对象的构建:当对象的构建过程比较复杂,包含多个部件,并且构建过程中的顺序和逻辑可能会发生变化时,可以使用建造者模式来封装构建过程,提高可维护性和扩展性。例如,构建一个包含多个部件的电子产品或汽车。
- 创建多个相似但不同表示的对象:当需要创建多个相似但不同表示的对象时,可以使用建造者模式来重复利用相同的构建过程,避免代码重复。例如,创建多种口味的冰淇淋,每种口味的构建过程相似但不同。
- 根据不同的构建过程创建不同的表示:当需要根据不同的构建过程来创建不同的表示时,可以使用建造者模式来动态选择构建过程,实现灵活的构建。例如,根据用户选择的选项构建不同配置的计算机或汽车。
- 隐藏产品构建细节:当需要隐藏产品构建的细节,只向客户端暴露简单的构建接口时,可以使用建造者模式。客户端只需与指挥者交互,无需关心产品的具体构建过程。
-
优点
- 封装复杂对象的构建过程:建造者模式将复杂对象的构建过程与其表示分离,将对象的构建过程封装在具体建造者类中,使得客户端无需关心对象的具体构建细节。
- 重复利用相同的构建过程:建造者模式可以重复利用相同的构建过程来创建多个相似但不同表示的对象,避免了代码重复。
- 灵活性和可扩展性:建造者模式通过指挥者和具体建造者的组合,可以动态选择构建过程,实现不同表示的创建,从而提高了系统的灵活性和可扩展性。
- 隐藏产品构建细节:建造者模式将产品的构建细节隐藏在具体建造者类中,只向客户端暴露简单的构建接口,降低了客户端与产品的耦合度。
-
缺点
- 增加了系统的复杂度:建造者模式引入了指挥者和具体建造者类,增加了系统的复杂度,可能会导致类的数量增加。
- 需要额外的开发工作:建造者模式需要定义指挥者和具体建造者类,并实现产品的构建过程,需要额外的开发工作。
- 不适用于简单对象的构建:建造者模式适用于复杂对象的构建,对于简单对象的构建可能会显得过于繁琐。
- 增加了对象创建的开销:由于建造者模式需要构建对象的各个部件,可能会增加对象创建的开销,特别是对于需要创建大量对象的情况。
-
示例
package mainimport "fmt"// 产品:汽车
type Car struct {Brand string // 汽车品牌Engine string // 汽车发动机型号Color string // 汽车颜色
}// 建造者接口
type Builder interface {SetBrand() // 设置汽车品牌SetEngine() // 设置汽车发动机型号SetColor() // 设置汽车颜色GetResult() *Car // 获取构建结果
}// 具体建造者:小汽车建造者
type SmallCarBuilder struct {car *Car
}// 实现建造者接口
func (s *SmallCarBuilder) SetBrand() {s.car.Brand = "小汽车品牌"
}func (s *SmallCarBuilder) SetEngine() {s.car.Engine = "1.0L 发动机"
}func (s *SmallCarBuilder) SetColor() {s.car.Color = "红色"
}func (s *SmallCarBuilder) GetResult() *Car {return s.car
}// 指挥者:汽车厂
type CarFactory struct {builder Builder // 使用的建造者
}// 构造函数
func NewCarFactory(builder Builder) *CarFactory {return &CarFactory{builder: builder}
}// 构建汽车的方法
func (c *CarFactory) Construct() *Car {// 初始化汽车对象c.builder.GetResult()c.builder.SetBrand() // 设置汽车品牌c.builder.SetEngine() // 设置汽车发动机型号c.builder.SetColor() // 设置汽车颜色return c.builder.GetResult() // 返回构建结果
}func main() {// 创建小汽车建造者smallCarBuilder := &SmallCarBuilder{car: &Car{}}// 创建汽车厂并指定建造者carFactory := NewCarFactory(smallCarBuilder)// 构建汽车car := carFactory.Construct()// 输出汽车配置信息fmt.Println("汽车配置:")fmt.Println("品牌:", car.Brand)fmt.Println("发动机:", car.Engine)fmt.Println("颜色:", car.Color)
}
- 输出结果
汽车配置:
品牌: 小汽车品牌
发动机: 1.0L 发动机
颜色: 红色