golang24种设计模式——创建型模式

设计模式(Design Pattern)是软件工程中针对常见问题的通用解决方案。

它们不是具体的代码,而是经过验证的最佳实践,帮助开发者设计出灵活、可维护和可扩展的软件系统

使用设计模式的好处

  1. 面试
  2. 提高代码复用性,写出高质量代码
  3. 前人总结的最佳实践,在合适的地方用合适的设计模式,可以事半功倍

创建型模式

在 Go 语言中,创建型模式(Creational Patterns)是一类用于处理对象创建的设计模式。它们的主要目标是提供一种灵活的方式来创建对象,同时隐藏对象创建的具体细节,从而降低代码的耦合度,并提高代码的可复用性和可维护性。

比如http.NewRequest()bytes.NewReader()md5.New()

创建型模式的核心思想是将对象的创建与使用分离,使得系统不依赖于具体的对象创建方式,而是依赖于抽象。

单例模式 Singleton

确保一个类只有一个实例,并提供一个全局访问点。

适用场景:

  • 配置管理、日志记录、数据库连接池等需要全局唯一实例的场景。

之前在项目里面,我们之前一直在用的global.DB,global.Config其实和这个差不多,但是它不是单例模式,因为是直接使用的对应的对象,而不是通过函数返回的对象

package main

import (
  "fmt"
)

type DB struct {
  Database string
}

var db *DB

func GetDB() *DB {
  db = &DB{
    Database: "fengfeng",
  }
  return db
}

func main() {
  d := db
  fmt.Printf("%p, %v\n", d, d.Database)
}


之前是直接使用全局变量,但是这个变量是存在为nil的情况的,这种情况下再使用它就是为出现空指针的情况

那么可以使用一个函数,在里面判断一下,如果这个对象是nil,就去初始化对象,后续如果有的话,就直接返回之前的那个创建好的对象

package main

import (
  "fmt"
  "sync"
)

type DB struct {
  Database string
}

var db *DB
var once sync.Once

func GetDB() *DB {
  once.Do(func() {
    db = &DB{
      Database: "fengfeng",
    }
  })
  return db
}

func main() {
  d := GetDB()
  fmt.Printf("%p, %v\n", d, d.Database)
  d = GetDB()
  fmt.Printf("%p, %v\n", d, d.Database)
}

简单工厂模式 Simple Factory Pattern

它们的目标都是将对象的创建与使用分离,从而降低代码的耦合度

但是具体的工厂还是有区别

模式特点适用场景
简单工厂一个工厂类负责创建所有产品,通过条件判断决定创建哪种产品。产品种类较少,创建逻辑简单。
工厂方法模式每个产品对应一个工厂类,符合开闭原则。产品种类较多,创建逻辑复杂。
抽象工厂模式每个工厂类可以创建一组相关产品,强调产品族的概念。需要创建一组相关对象的场景。

简单工厂并不是一个正式的设计模式,而是一种编程习惯。它通过一个工厂类来封装对象的创建逻辑,客户端只需要传递参数给工厂类,由工厂类决定创建哪种对象。

特点

  • 只有一个工厂类,负责创建所有产品。
  • 通过条件判断(如 switchif-else)来决定创建哪种产品。

适用场景

  • 产品种类较少,且创建逻辑简单的场景。

开闭原则:当需求发生变化时,可以通过增加新的代码来扩展系统的功能,而不是修改现有的代码。

package main

import "fmt"

type Product interface {
  Use()
}

type ProductA struct{}

func (p *ProductA) Use() {
  fmt.Println("Using Product A")
}

type ProductB struct{}

func (p *ProductB) Use() {
  fmt.Println("Using Product B")
}

func CreateProduct(productType string) Product {
  switch productType {
  case "A":
    return &ProductA{}
  case "B":
    return &ProductB{}
  default:
    return nil
  }
}

func main() {
  productA := CreateProduct("A")
  productA.Use()

  productB := CreateProduct("B")
  productB.Use()
}


优点

  • 简单易用,适合小型项目。

缺点

  • 不符合开闭原则(OCP),新增产品时需要修改工厂类。

工厂方法模式 Factory Method

之前的简单工厂模式,一个工厂就负责了好几个产品的生产

工厂方法模式则是定义了一个创建对象的接口,但将具体的创建逻辑延迟到子类中。每个子类负责创建一种具体的产品。

特点

  • 每个产品对应一个工厂类。
  • 符合开闭原则,新增产品时只需增加新的工厂类,无需修改现有代码。

适用场景

  • 产品种类较多,且创建逻辑复杂的场景。
package main

import "fmt"

type Database interface {
  Connect() string
}

// MySQL 操作
type MySQL struct{}

func (m *MySQL) Connect() string {
  return "Connected to MySQL"
}

// PostgreSQL 操作
type PostgreSQL struct{}

func (p *PostgreSQL) Connect() string {
  return "Connected to PostgreSQL"
}

// DatabaseFactory 工厂接口
type DatabaseFactory interface {
  CreateDatabase() Database
}

// MySQLFactory MySQL 工厂
type MySQLFactory struct{}

func (m *MySQLFactory) CreateDatabase() Database {
  return &MySQL{}
}

// PostgreSQL 工厂
type PostgreSQLFactory struct{}

func (p *PostgreSQLFactory) CreateDatabase() Database {
  return &PostgreSQL{}
}

// UseDatabase 使用数据库
func UseDatabase(factory DatabaseFactory) {
  db := factory.CreateDatabase()
  fmt.Println(db.Connect())
}
func main() {
  // 使用 MySQL
  mysqlFactory := &MySQLFactory{}
  UseDatabase(mysqlFactory)

  // 使用 PostgreSQL
  postgresFactory := &PostgreSQLFactory{}
  UseDatabase(postgresFactory)
}


优点

  • 符合开闭原则,扩展性强。
  • 每个工厂只负责一种产品的创建,职责单一。

缺点

  • 类的数量会增加,系统复杂度提高。

抽象工厂模式 Abstract Factory

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。它适用于需要创建一组相关产品的场景。

特点

  • 每个工厂类可以创建多个相关产品。
  • 强调产品族的概念,例如 GUI 库中的不同风格组件(Windows 风格、Mac 风格)。

适用场景

  • 需要创建一组相关对象的场景。
package main

import "fmt"

// DBConnection 抽象产品:数据库连接接口
type DBConnection interface {
  Connect() string
}

// DBCommand 抽象产品:数据库命令接口
type DBCommand interface {
  Execute(query string) string
}

// MySQLConnection 具体产品:MySQL 连接
type MySQLConnection struct{}

func (m *MySQLConnection) Connect() string {
  return "Connected to MySQL"
}

// MySQLCommand 具体产品:MySQL 命令
type MySQLCommand struct{}

func (m *MySQLCommand) Execute(query string) string {
  return fmt.Sprintf("MySQL executing query: %s", query)
}

// PostgreSQLConnection 具体产品:PostgreSQL 连接
type PostgreSQLConnection struct{}

func (p *PostgreSQLConnection) Connect() string {
  return "Connected to PostgreSQL"
}

// PostgreSQLCommand 具体产品:PostgreSQL 命令
type PostgreSQLCommand struct{}

func (p *PostgreSQLCommand) Execute(query string) string {
  return fmt.Sprintf("PostgreSQL executing query: %s", query)
}

// DBFactory 抽象工厂接口
type DBFactory interface {
  CreateConnection() DBConnection
  CreateCommand() DBCommand
}

// MySQLFactory 具体工厂:MySQL 工厂
type MySQLFactory struct{}

func (m *MySQLFactory) CreateConnection() DBConnection {
  return &MySQLConnection{}
}

func (m *MySQLFactory) CreateCommand() DBCommand {
  return &MySQLCommand{}
}

// PostgreSQLFactory 具体工厂:PostgreSQL 工厂
type PostgreSQLFactory struct{}

func (p *PostgreSQLFactory) CreateConnection() DBConnection {
  return &PostgreSQLConnection{}
}

func (p *PostgreSQLFactory) CreateCommand() DBCommand {
  return &PostgreSQLCommand{}
}

func UseDatabase(factory DBFactory) {
  connection := factory.CreateConnection()
  command := factory.CreateCommand()

  fmt.Println(connection.Connect())
  fmt.Println(command.Execute("SELECT * FROM users"))
}

func main() {
  // 使用 MySQL
  mysqlFactory := &MySQLFactory{}
  UseDatabase(mysqlFactory)

  // 使用 PostgreSQL
  postgresFactory := &PostgreSQLFactory{}
  UseDatabase(postgresFactory)
}

优点

  • 可以创建一组相关对象,保证对象之间的兼容性。
  • 符合开闭原则,扩展性强。

缺点

  • 类的数量会增加,系统复杂度提高。
  • 新增产品族或产品等级结构时,需要修改抽象工厂接口及其所有实现类。
抽象工厂模式和工厂方法模式的区别
特性工厂方法模式抽象工厂模式
产品数量一个工厂方法只创建一个产品。一个抽象工厂创建多个相关产品(产品族)。
产品关系产品之间没有直接关系。产品之间是相关的(属于同一个产品族)。
扩展性扩展时需要新增具体工厂类。扩展时需要新增具体工厂类和产品族。
实现方式通过继承实现。通过组合实现。
适用场景单一产品的创建。多个相关产品的创建。
  • 工厂方法模式 更简单,适用于单一产品的创建。
  • 抽象工厂模式 更强大,适用于创建多个相关产品,但实现也更复杂。

建造者模式 Builder

它用于分步构建复杂对象。建造者模式的核心思想是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式特别适用于以下场景:

  • 对象的构建过程非常复杂,包含多个步骤。
  • 对象的构建过程需要支持不同的配置或表示。
package main

import "fmt"

// 产品:House
type House struct {
  Walls   string
  Roof    string
  Windows string
  Doors   string
}

func (h *House) Show() {
  fmt.Printf("House with %s walls, %s roof, %s windows, and %s doors\n",
    h.Walls, h.Roof, h.Windows, h.Doors)
}

// 建造者接口
type HouseBuilder interface {
  BuildWalls()
  BuildRoof()
  BuildWindows()
  BuildDoors()
  GetHouse() *House
}

// 具体建造者:ConcreteHouseBuilder
type ConcreteHouseBuilder struct {
  house *House
}

func NewConcreteHouseBuilder() *ConcreteHouseBuilder {
  return &ConcreteHouseBuilder{house: &House{}}
}

func (b *ConcreteHouseBuilder) BuildWalls() {
  b.house.Walls = "concrete"
}

func (b *ConcreteHouseBuilder) BuildRoof() {
  b.house.Roof = "tile"
}

func (b *ConcreteHouseBuilder) BuildWindows() {
  b.house.Windows = "glass"
}

func (b *ConcreteHouseBuilder) BuildDoors() {
  b.house.Doors = "wooden"
}

func (b *ConcreteHouseBuilder) GetHouse() *House {
  return b.house
}

// 指挥者
type Director struct {
  builder HouseBuilder
}

func NewDirector(builder HouseBuilder) *Director {
  return &Director{builder: builder}
}

func (d *Director) Construct() {
  d.builder.BuildWalls()
  d.builder.BuildRoof()
  d.builder.BuildWindows()
  d.builder.BuildDoors()
}

// 客户端代码
func main() {
  // 创建具体建造者
  builder := NewConcreteHouseBuilder()

  // 创建指挥者
  director := NewDirector(builder)

  // 指挥者构建产品
  director.Construct()

  // 获取最终产品
  house := builder.GetHouse()
  house.Show()
}

原型模式 Prototype

它通过复制现有对象来创建新对象,而不是通过新建类的方式。原型模式的核心思想是利用对象的克隆能力,避免重复初始化,特别适用于创建成本较高的对象。

package main

import "fmt"

// Prototype 原型接口
type Prototype interface {
  Clone() Prototype
}

// ConcretePrototype 具体原型
type ConcretePrototype struct {
  Name string
  Age  int
}

func (p *ConcretePrototype) Clone() Prototype {
  // 创建一个新对象,并复制当前对象的属性
  return &ConcretePrototype{
    Name: p.Name,
    Age:  p.Age,
  }
}

func (p *ConcretePrototype) String() string {
  return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
}

func main() {
  // 创建原型对象
  prototype := &ConcretePrototype{
    Name: "Alice",
    Age:  25,
  }

  // 克隆原型对象
  clone := prototype.Clone().(*ConcretePrototype)

  // 修改克隆对象的属性
  clone.Name = "Bob"
  clone.Age = 30

  // 输出原型对象和克隆对象
  fmt.Println("Prototype:", prototype)
  fmt.Println("Clone:", clone)
}

使用原型模式,如果有引用类型,则需要考虑深拷贝和浅拷贝的问题

浅拷贝只复制对象本身而不复制其引用的对象,深拷贝则会递归地复制整个对象图。

这需要根据需求选择适当的拷贝方式

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值