设计模式(Design Pattern)是软件工程中针对常见问题的通用解决方案。
它们不是具体的代码,而是经过验证的最佳实践,帮助开发者设计出灵活、可维护和可扩展的软件系统
使用设计模式的好处
- 面试
- 提高代码复用性,写出高质量代码
- 前人总结的最佳实践,在合适的地方用合适的设计模式,可以事半功倍
创建型模式
在 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
它们的目标都是将对象的创建与使用分离,从而降低代码的耦合度
但是具体的工厂还是有区别
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 简单工厂 | 一个工厂类负责创建所有产品,通过条件判断决定创建哪种产品。 | 产品种类较少,创建逻辑简单。 |
| 工厂方法模式 | 每个产品对应一个工厂类,符合开闭原则。 | 产品种类较多,创建逻辑复杂。 |
| 抽象工厂模式 | 每个工厂类可以创建一组相关产品,强调产品族的概念。 | 需要创建一组相关对象的场景。 |
简单工厂并不是一个正式的设计模式,而是一种编程习惯。它通过一个工厂类来封装对象的创建逻辑,客户端只需要传递参数给工厂类,由工厂类决定创建哪种对象。
特点:
- 只有一个工厂类,负责创建所有产品。
- 通过条件判断(如
switch或if-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)
}
使用原型模式,如果有引用类型,则需要考虑深拷贝和浅拷贝的问题
浅拷贝只复制对象本身而不复制其引用的对象,深拷贝则会递归地复制整个对象图。
这需要根据需求选择适当的拷贝方式
698

被折叠的 条评论
为什么被折叠?



