设计模式(Design Pattern)是软件工程中针对常见问题的通用解决方案。
它们不是具体的代码,而是经过验证的最佳实践,帮助开发者设计出灵活、可维护和可扩展的软件系统
使用设计模式的好处
- 面试
 - 提高代码复用性,写出高质量代码
 - 前人总结的最佳实践,在合适的地方用合适的设计模式,可以事半功倍
 
创建型模式
在 Go 语言中,创建型模式(Creational Patterns)是一类用于处理对象创建的设计模式。它们的主要目标是提供一种灵活的方式来创建对象,同时隐藏对象创建的具体细节,从而降低代码的耦合度,并提高代码的可复用性和可维护性。
比如http.NewRequest(),bytes.NewReader(),md5.New()
创建型模式的核心思想是将对象的创建与使用分离,使得系统不依赖于具体的对象创建方式,而是依赖于抽象。
单例模式 Singleton
确保一个类只有一个实例,并提供一个全局访问点。
适用场景:
- 配置管理、日志记录、数据库连接池等需要全局唯一实例的场景。
 
之前在项目里面,我们之前一直在用的global.DB,global.Config其实和这个差不多,但是它不是单例模式,因为是直接使用的对应的对象,而不是通过函数返回的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | 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,就去初始化对象,后续如果有的话,就直接返回之前的那个创建好的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | 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
它们的目标都是将对象的创建与使用分离,从而降低代码的耦合度
但是具体的工厂还是有区别
| 模式 | 特点 | 适用场景 | 
|---|
| 简单工厂 | 一个工厂类负责创建所有产品,通过条件判断决定创建哪种产品。 | 产品种类较少,创建逻辑简单。 | 
| 工厂方法模式 | 每个产品对应一个工厂类,符合开闭原则。 | 产品种类较多,创建逻辑复杂。 | 
| 抽象工厂模式 | 每个工厂类可以创建一组相关产品,强调产品族的概念。 | 需要创建一组相关对象的场景。 | 
简单工厂并不是一个正式的设计模式,而是一种编程习惯。它通过一个工厂类来封装对象的创建逻辑,客户端只需要传递参数给工厂类,由工厂类决定创建哪种对象。
特点:
- 只有一个工厂类,负责创建所有产品。
 - 通过条件判断(如 
switch 或 if-else)来决定创建哪种产品。 
适用场景:
开闭原则:当需求发生变化时,可以通过增加新的代码来扩展系统的功能,而不是修改现有的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
   | 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
之前的简单工厂模式,一个工厂就负责了好几个产品的生产
工厂方法模式则是定义了一个创建对象的接口,但将具体的创建逻辑延迟到子类中。每个子类负责创建一种具体的产品。
特点:
- 每个产品对应一个工厂类。
 - 符合开闭原则,新增产品时只需增加新的工厂类,无需修改现有代码。
 
适用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | package main
  import "fmt"
  type Database interface {   Connect() string }
 
  type MySQL struct{}
  func (m *MySQL) Connect() string {   return "Connected to MySQL" }
 
  type PostgreSQL struct{}
  func (p *PostgreSQL) Connect() string {   return "Connected to PostgreSQL" }
 
  type DatabaseFactory interface {   CreateDatabase() Database }
 
  type MySQLFactory struct{}
  func (m *MySQLFactory) CreateDatabase() Database {   return &MySQL{} }
 
  type PostgreSQLFactory struct{}
  func (p *PostgreSQLFactory) CreateDatabase() Database {   return &PostgreSQL{} }
 
  func UseDatabase(factory DatabaseFactory) {   db := factory.CreateDatabase()   fmt.Println(db.Connect()) } func main() {      mysqlFactory := &MySQLFactory{}   UseDatabase(mysqlFactory)
       postgresFactory := &PostgreSQLFactory{}   UseDatabase(postgresFactory) }
   | 
优点:
- 符合开闭原则,扩展性强。
 - 每个工厂只负责一种产品的创建,职责单一。
 
缺点:
抽象工厂模式 Abstract Factory
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。它适用于需要创建一组相关产品的场景。
特点:
- 每个工厂类可以创建多个相关产品。
 - 强调产品族的概念,例如 GUI 库中的不同风格组件(Windows 风格、Mac 风格)。
 
适用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
   | package main
  import "fmt"
 
  type DBConnection interface {   Connect() string }
 
  type DBCommand interface {   Execute(query string) string }
 
  type MySQLConnection struct{}
  func (m *MySQLConnection) Connect() string {   return "Connected to MySQL" }
 
  type MySQLCommand struct{}
  func (m *MySQLCommand) Execute(query string) string {   return fmt.Sprintf("MySQL executing query: %s", query) }
 
  type PostgreSQLConnection struct{}
  func (p *PostgreSQLConnection) Connect() string {   return "Connected to PostgreSQL" }
 
  type PostgreSQLCommand struct{}
  func (p *PostgreSQLCommand) Execute(query string) string {   return fmt.Sprintf("PostgreSQL executing query: %s", query) }
 
  type DBFactory interface {   CreateConnection() DBConnection   CreateCommand() DBCommand }
 
  type MySQLFactory struct{}
  func (m *MySQLFactory) CreateConnection() DBConnection {   return &MySQLConnection{} }
  func (m *MySQLFactory) CreateCommand() DBCommand {   return &MySQLCommand{} }
 
  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() {      mysqlFactory := &MySQLFactory{}   UseDatabase(mysqlFactory)
       postgresFactory := &PostgreSQLFactory{}   UseDatabase(postgresFactory) }
   | 
优点:
- 可以创建一组相关对象,保证对象之间的兼容性。
 - 符合开闭原则,扩展性强。
 
缺点:
- 类的数量会增加,系统复杂度提高。
 - 新增产品族或产品等级结构时,需要修改抽象工厂接口及其所有实现类。
 
抽象工厂模式和工厂方法模式的区别
| 特性 | 工厂方法模式 | 抽象工厂模式 | 
|---|
| 产品数量 | 一个工厂方法只创建一个产品。 | 一个抽象工厂创建多个相关产品(产品族)。 | 
| 产品关系 | 产品之间没有直接关系。 | 产品之间是相关的(属于同一个产品族)。 | 
| 扩展性 | 扩展时需要新增具体工厂类。 | 扩展时需要新增具体工厂类和产品族。 | 
| 实现方式 | 通过继承实现。 | 通过组合实现。 | 
| 适用场景 | 单一产品的创建。 | 多个相关产品的创建。 | 
- 工厂方法模式 更简单,适用于单一产品的创建。
 - 抽象工厂模式 更强大,适用于创建多个相关产品,但实现也更复杂。
 
建造者模式 Builder
它用于分步构建复杂对象。建造者模式的核心思想是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式特别适用于以下场景:
- 对象的构建过程非常复杂,包含多个步骤。
 - 对象的构建过程需要支持不同的配置或表示。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
   | package main
  import "fmt"
 
  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 }
 
  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
它通过复制现有对象来创建新对象,而不是通过新建类的方式。原型模式的核心思想是利用对象的克隆能力,避免重复初始化,特别适用于创建成本较高的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | package main
  import "fmt"
 
  type Prototype interface {   Clone() Prototype }
 
  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) }
   | 
使用原型模式,如果有引用类型,则需要考虑深拷贝和浅拷贝的问题
浅拷贝只复制对象本身而不复制其引用的对象,深拷贝则会递归地复制整个对象图。
这需要根据需求选择适当的拷贝方式
参考:
Go24种设计模式——创建型模式