Golang 设计模式

创建型模式

简单工厂模式

简单工厂模式是一种创建型设计模式。它定义了一个工厂类,这个工厂类负责创建产品对象。客户端(使用产品的代码)只需要向工厂类请求所需的产品,而不需要知道产品是如何创建的,这样就将产品的创建和使用分离,使得代码的结构更加清晰。

假设我们要创建一个图形绘制工具,首先定义一个图形接口,然后有不同的图形类实现这个接口。

图形接口

go 复制代码
type Shape interface {
    Draw()
}

具体图形类:圆形

go 复制代码
type Circle struct{}

func (c *Circle) Draw() {
    println("Drawing a circle")
}

具体图形类:矩形

go 复制代码
type Rectangle struct{}

func (r *Rectangle) Draw() {
    println("Drawing a rectangle")
}

工厂类定义

接着创建简单工厂类,用于生产不同的图形。

go 复制代码
  type ShapeFactory struct{}

  func (f *ShapeFactory) CreateShape(shapeType string) Shape {
      if shapeType == "circle" {
          return &Circle{}
      } else if shapeType == "rectangle" {
          return &Rectangle{}
      }
      return nil
  }

使用简单工厂模式

在主函数或者其他客户端代码中,可以这样使用工厂类来获取产品(图形)并使用。

go 复制代码
  func main() {
      factory := &ShapeFactory{}
      circle := factory.CreateShape("circle")
      circle.Draw()

      rectangle := factory.CreateShape("rectangle")
      rectangle.Draw()
  }

抽象工厂模式

抽象工厂方法模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离,使得代码更具灵活性和可维护性。其核心在于有一个抽象工厂接口,该接口定义了创建一系列相关产品对象的抽象方法,然后由具体的工厂类来实现这些抽象方法,创建具体的产品对象。

1. 定义产品接口

首先,定义一系列相关的产品接口,比如这里假设有两种产品:ProductA和ProductB。

go 复制代码
// ProductA接口,代表产品A的抽象
type ProductA interface {
    Use()
}

// ProductB接口,代表产品B的抽象
type ProductB interface {
    Use()
}

接口中定义了Use方法,用于表示产品的使用行为,具体的产品实现类需要实现这个方法。

2. 定义具体产品实现类

接着,创建具体的产品实现类,分别实现ProductA和ProductB接口。

go 复制代码
// ConcreteProductA1是ProductA接口的一个具体实现
type ConcreteProductA1 struct{}

func (p *ConcreteProductA1) Use() {
    println("Using ConcreteProductA1")
}

// ConcreteProductA2是ProductA接口的另一个具体实现
type ConcreteProductA2 struct{}

func (p *ConcreteProductA2) Use() {
    println("Using ConcreteProductA2")
}

// ConcreteProductB1是ProductB接口的一个具体实现
type ConcreteProductB1 struct{}

func (p *ConcreteProductB1) Use() {
    println("Using ConcreteProductB1")
}

// ConcreteProductB2是ProductB接口的另一个具体实现
type ConcreteProductB2 struct{}

func (p *ConcreteProductB2) Use() {
    println("Using ConcreteProductB2")
}

这些具体产品类实现了各自接口的Use方法,定义了具体的使用行为,比如打印出不同的使用提示信息。

3. 定义抽象工厂接口

然后,定义抽象工厂接口,它声明了创建ProductA和ProductB的抽象方法。

go 复制代码
// AbstractFactory接口,定义创建产品的抽象方法
type AbstractFactory interface {
    CreateProductA() ProductA
    CreateProductB() ProductB
}

4. 定义具体工厂实现类

再创建具体的工厂实现类,实现抽象工厂接口,根据具体工厂的逻辑来创建对应的具体产品。

go 复制代码
// ConcreteFactory1是AbstractFactory接口的一个具体实现,用于创建特定组合的产品
type ConcreteFactory1 struct{}

func (f *ConcreteFactory1) CreateProductA() ProductA {
    return &ConcreteProductA1{}
}

func (f *ConcreteFactory1) CreateProductB() ProductB {
    return &ConcreteProductB1{}
}

// ConcreteFactory2是AbstractFactory接口的另一个具体实现,用于创建另一种组合的产品
type ConcreteFactory2 struct{}

func (f *ConcreteFactory2) CreateProductA() ProductA {
    return &ConcreteProductA2{}
}

func (f *ConcreteFactory2) CreateProductB() ProductB {
    return &ConcreteProductB2{}
}

在这些具体工厂类中,实现了CreateProductA和CreateProductB方法,分别返回对应的具体产品实例。

5. 使用抽象工厂创建对象并使用产品

go 复制代码
func main() {
    // 使用ConcreteFactory1创建产品
    factory1 := &ConcreteFactory1{}
    productA1 := factory1.CreateProductA()
    productB1 := factory1.CreateProductB()
    productA1.Use()
    productB1.Use()

    // 使用ConcreteFactory2创建产品
    factory2 := &ConcreteFactory2{}
    productA2 := factory2.CreateProductA()
    productB2 := factory2.CreateProductB()
    productA2.Use()
    productB2.Use()
}

在main函数中,首先实例化了不同的具体工厂(这里是ConcreteFactory1和ConcreteFactory2),然后通过这些工厂分别创建对应的产品实例,并调用产品的Use方法来展示其使用行为。

优点

  • 解耦对象创建和使用:使得代码中创建对象的部分和使用对象的部分相互独立,便于维护和扩展。例如,当需要添加新的具体产品或者改变产品的创建逻辑时,只需要在对应的工厂类和产品类中修改,而不会影响到使用这些产品的其他代码。
  • 便于代码的复用和替换:可以方便地替换具体的工厂实现类,从而改变创建的产品组合,在不同的应用场景下复用相同的产品接口和使用逻辑。
    缺点
  • 增加代码复杂度:由于引入了抽象工厂、具体工厂、产品接口以及多个具体产品类等多层结构,代码的整体复杂度会有所增加,对于简单的应用场景可能显得有些 "大材小用"。
  • 不利于理解:对于不熟悉设计模式的开发者来说,初次接触抽象工厂方法模式时,理解其结构和运行流程可能需要花费一些时间。

创建者模式

创建者模式是一种创建型设计模式,它将一个复杂对象的构建过程和它的表示分离,使得同样的构建过程可以创建不同的表示。该模式适用于创建对象的过程较为复杂,需要多个步骤来完成,并且不同的配置或步骤组合能够生成不同形态的对象的场景。

1. 定义产品结构体

首先,定义要构建的复杂产品对象,这里以一个简单的 Computer(电脑)结构体为例,它包含多个部件属性。

go 复制代码
type Computer struct {
    CPU    string
    Memory string
    Storage string
    GPU    string
}

2. 定义创建者接口和具体创建者结构体

接着,创建创建者接口,它声明了构建产品各个部件以及获取最终产品的抽象方法。然后创建具体的创建者结构体,实现这些抽象方法来定义具体的构建逻辑。

go 复制代码
// Builder接口,定义构建电脑各部件及获取最终电脑的方法
type Builder interface {
    BuildCPU()
    BuildMemory()
    BuildStorage()
    BuildGPU()
    GetComputer() *Computer
}

// GamingComputerBuilder是实现Builder接口的具体创建者结构体,用于构建游戏电脑
type GamingComputerBuilder struct {
    computer Computer
}

func (b *GamingComputerBuilder) BuildCPU() {
    b.computer.CPU = "Intel Core i9"
}

func (b *GamingComputerBuilder) BuildMemory() {
    b.computer.Memory = "32GB DDR5"
}

func (b *GamingComputerBuilder) BuildStorage() {
    b.computer.Storage = "1TB NVMe SSD"
}

func (b *GamingComputerBuilder) BuildGPU() {
    b.computer.GPU = "NVIDIA RTX 4090"
}

func (b *GamingComputerBuilder) GetComputer() *Computer {
    return &b.computer
}

// OfficeComputerBuilder是实现Builder接口的另一个具体创建者结构体,用于构建办公电脑
type OfficeComputerBuilder struct {
    computer Computer
}

func (b *OfficeComputerBuilder) BuildCPU() {
    b.computer.CPU = "Intel Core i5"
}

func (b *OfficeComputerBuilder) BuildMemory() {
    b.computer.Memory = "16GB DDR4"
}

func (b *OfficeComputerBuilder) BuildStorage() {
    b.computer.Storage = "512GB SATA SSD"
}

func (b *OfficeComputerBuilder) BuildGPU() {
    b.computer.GPU = "Integrated Graphics"
}

func (b *OfficeComputerBuilder) GetComputer() *Computer {
    return &b.computer
}

在上述代码中,GamingComputerBuilder 按照游戏电脑的配置来构建各个部件,而 OfficeComputerBuilder 则按照办公电脑的配置进行构建,它们都实现了 Builder 接口的抽象方法,最后通过 GetComputer 方法返回构建好的电脑对象。

3. 定义指挥者结构体

指挥者结构体用于控制构建过程,它接收一个创建者对象,并按照特定顺序调用创建者的构建方法来完成产品的构建。

go 复制代码
// Director结构体,用于协调构建过程
type Director struct {
    builder Builder
}

func (d *Director) Construct() *Computer {
    d.builder.BuildCPU()
    d.builder.BuildMemory()
    d.builder.BuildStorage()
    d.builder.BuildGPU()
    return d.builder.GetComputer()
}

4. 使用创建者模式构建对象

最后,在 main 函数中展示如何使用创建者模式来构建不同类型的电脑对象。

go 复制代码
func main() {
    // 创建游戏电脑
    gamingBuilder := &GamingComputerBuilder{}
    gamingDirector := &Director{builder: gamingBuilder}
    gamingComputer := gamingDirector.Construct()
    println("Gaming Computer Configuration:")
    println("CPU:", gamingComputer.CPU)
    println("Memory:", gamingComputer.Memory)
    println("Storage:", gamingComputer.Storage)
    println("GPU:", gamingComputer.GPU)

    // 创建办公电脑
    officeBuilder := &OfficeComputerBuilder{}
    officeDirector := &Director{builder: officeBuilder}
    officeComputer := officeDirector.Construct()
    println("\nOffice Computer Configuration:")
    println("CPU:", officeComputer.CPU)
    println("Memory:", officeComputer.Memory)
    println("Storage:", officeComputer.Storage)
    println("GPU:", officeComputer.GPU)
}

在 main 函数中,先分别实例化了游戏电脑和办公电脑对应的创建者和指挥者,然后通过指挥者调用构建过程来获取构建好的电脑对象,并打印出它们的配置信息。

优点

  • 解耦构建过程和产品表示:使得构建复杂对象的代码与对象本身的结构和表示分离,方便对构建过程进行修改和扩展,而不影响产品的使用逻辑。
  • 便于精细化控制构建过程:可以通过指挥者精确地控制构建步骤的顺序,并且能够根据不同需求灵活地使用不同的创建者来构建多种形态的产品。
  • 提高代码可读性和可维护性:将复杂的构建逻辑封装在创建者和指挥者中,使得代码结构更加清晰,易于理解和维护,尤其是在构建过程涉及多个步骤和多种配置的情况下。
    缺点
  • 增加代码复杂度:引入了创建者、指挥者等额外的结构体和接口,相比简单直接创建对象的方式,代码结构变得更复杂,对于简单的创建对象场景可能会显得有些繁琐。
  • 需要额外的代码管理:要维护创建者、指挥者以及它们与产品之间的关系,需要更多的代码来进行管理和协调,增加了一定的开发成本。

原型模式

原型模式是一种创建型设计模式。它的核心思想是通过复制一个已经存在的对象(原型)来创建新的对象,而不是通过传统的使用构造函数来初始化对象。这就好比是克隆,以一个原始对象为蓝本,快速制造出与其相似的新对象。

定义原型接口和实现类:

首先定义一个原型接口,这个接口有一个方法用于克隆对象。假设我们要创建一个图形的原型模式,接口如下:

go 复制代码
type Shape interface {
    Clone() Shape
}

然后创建具体的图形类实现这个接口。以矩形(Rectangle)为例:

go 复制代码
type Rectangle struct {
    width  float64
    height float64
}
func (r *Rectangle) Clone() Shape {
    return &Rectangle{
        width:  r.width,
        height: r.height,
    }
}

使用原型模式创建对象:

在主函数或者其他需要创建对象的地方,可以这样使用原型模式。

go 复制代码
func main() {
    originalRectangle := &Rectangle{width: 10, height: 20}
    clonedRectangle := originalRectangle.Clone().(*Rectangle)
    // 验证克隆后的对象是否和原对象相同(除了内存地址)
    if originalRectangle.width == clonedRectangle.width && originalRectangle.height == clonedRectangle.height {
        println("克隆成功")
    }
}

单例模式

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。就好像在一个应用程序中,某些资源(如数据库连接池、配置管理器等)只需要一个实例存在,以避免资源浪费或数据不一致等问题。

懒汉式单例(线程不安全)

这种方式是在第一次调用获取实例的方法时才创建实例。

go 复制代码
type Singleton struct{}

var instance *Singleton

func GetInstance() *Singleton {
    if instance == nil {
        instance = &Singleton{}
    }
    return instance
}

懒汉式单例(线程安全)

为了解决上述问题,可以使用互斥锁来保证在多线程环境下的安全。

go 复制代码
import (
    "sync"
)

type Singleton struct{}

var (
    instance *Singleton
    mutex    sync.Mutex
)

func GetInstance() *Singleton {
    mutex.Lock()
    defer mutex.Unlock()
    if instance == nil {
        instance = &Singleton{}
    }
    return instance
}

这里使用了sync.Mutex互斥锁,在获取实例的方法中,先加锁,然后判断实例是否为nil,如果是则创建实例,最后解锁。这样就保证了在多线程环境下只有一个实例被创建。

饿汉式单例

这种方式是在程序启动时就创建好实例,而不是等到第一次调用获取实例的方法时。

go 复制代码
type Singleton struct{}

var instance = &Singleton{}

func GetInstance() *Singleton {
    return instance
}

结构性模式

外观行模式

外观模式(Facade Pattern)是一种结构型设计模式。它为复杂的子系统提供了一个统一的、简化的接口,隐藏了子系统的复杂性和内部实现细节。就好像给一个复杂的机器(子系统)安装了一个简单的控制面板(外观类),用户通过操作这个控制面板就能轻松地使用机器,而无需了解机器内部复杂的结构和运作原理。

假设场景:家庭影院系统

家庭影院系统通常包括多个复杂的子系统,如 DVD 播放器、投影仪、音响系统等。首先定义这些子系统的接口和实现。

DVD 播放器接口和实现

go 复制代码
type DVDPlayer interface {
    On()
    Off()
    Play(movie string)
    Stop()
}

type dvdPlayer struct{}

func (d *dvdPlayer) On() {
    println("DVD player is on")
}

func (d *dvdPlayer) Off() {
    println("DVD player is off")
}

func (d *dvdPlayer) Play(movie string) {
    println("Playing movie:", movie)
}

func (d *dvdPlayer) Stop() {
    println("DVD player stopped")
}

投影仪接口和实现

go 复制代码
type Projector interface {
    On()
    Off()
    Project(image string)
}

type projector struct{}

func (p *projector) On() {
    println("Projector is on")
}

func (p *projector) Off() {
    println("Projector is off")
}

func (p *projector) Project(image string) {
    println("Projecting image:", image)
}

音响系统接口和实现

go 复制代码
type SoundSystem interface {
    On()
    Off()
    SetVolume(volume int)
}

type soundSystem struct{}

func (s *soundSystem) On() {
    println("Sound system is on")
}

func (s *soundSystem) Off() {
    println("Sound system is off")
}

func (s *soundSystem) SetVolume(volume int) {
    println("Setting sound volume to:", volume)
}

外观类实现

现在创建外观类来提供家庭影院系统的简单统一接口。

go 复制代码
type HomeTheaterFacade struct {
    dvdPlayer  DVDPlayer
    projector  Projector
    soundSystem SoundSystem
}

func NewHomeTheaterFacade() *HomeTheaterFacade {
    return &HomeTheaterFacade{
        dvdPlayer:  &dvdPlayer{},
        projector:  &projector{},
        soundSystem: &soundSystem{},
    }
}

func (h *HomeTheaterFacade) WatchMovie(movie string) {
    h.dvdPlayer.On()
    h.projector.On()
    h.soundSystem.On()
    h.soundSystem.SetVolume(10)
    h.dvdPlayer.Play(movie)
    h.projector.Project("Movie Image")
}

func (h *HomeTheaterFacade) EndMovie() {
    h.dvdPlayer.Stop()
    h.dvdPlayer.Off()
    h.projector.Off()
    h.soundSystem.Off()
}

使用外观模式

在主函数中,可以这样使用外观类来操作家庭影院系统。

go 复制代码
  func main() {
      homeTheater := NewHomeTheaterFacade()
      homeTheater.WatchMovie("Avatar")
      homeTheater.EndMovie()
  }
相关推荐
vker1 小时前
第 1 天:单例模式(Singleton Pattern)—— 创建型模式
java·设计模式
晨米酱19 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript