创建型模式(Creational Patterns)
设计模式概览
-
创建型模式(Creational Patterns):
-
单例模式(Singleton)
-
工厂方法模式(Factory Method)
-
抽象工厂模式(Abstract Factory)
-
建造者模式(Builder)
-
原型模式(Prototype)
-
-
结构型模式(Structural Patterns):
-
适配器模式(Adapter)
-
桥接模式(Bridge)
-
组合模式(Composite)
-
装饰者模式(Decorator)
-
外观模式(Facade)
-
享元模式(Flyweight)
-
代理模式(Proxy)
-
-
行为型模式(Behavioral Patterns):
-
责任链模式(Chain of Responsibility)
-
命令模式(Command)
-
解释器模式(Interpreter)
-
迭代器模式(Iterator)
-
中介者模式(Mediator)
-
备忘录模式(Memento)
-
观察者模式(Observer)
-
状态模式(State)
-
策略模式(Strategy)
-
模板方法模式(Template Method)
-
访问者模式(Visitor)
-
0. 简单工厂模式
简单工厂模式(Simple Factory Pattern)
又叫做静态工厂方法模式(Static Factory Method Pattern),是一种对象创建型模式,并不属于GoF 的23种设计模式之一,但是是学习其他工厂模式的的基础。它定义了一个工厂类,用于创建不同类型的对象。简单来说,简单工厂模式就是将对象的创建过程封装起来,使得客户端可以通过一个工厂类来创建不同类型的对象,而无需知道具体的创建过程。
实现简单工厂,通常需要定义一个工厂方法来创建不同类型的对象。这个工厂方法通常是一个静态方法,它接收一个参数来指定要创建的对象类型,并返回一个实例化的对象。
type MemTableType = int8
const (
Btree MemTableType = iota
ART
)
type MemTable interface {
// Put Stores the Pos information for key pairs in the index
Put(key, value []byte) bool
// Get Retrieve the Pos information based on the key
Get(key []byte) []byte
// Del Delete the Pos information based on the key
Del(key []byte) bool
}
type BTree struct {
tree *btree.BTree
lock *sync.RWMutex
}
// NewBTree Init BTree struct
func NewBTree() *BTree {
return &BTree{
tree: btree.New(32),
lock: new(sync.RWMutex),
}
}
type AdaptiveRadixTree struct {
tree art.Tree
lock *sync.RWMutex
}
func NewAdaptiveRadixTree() *AdaptiveRadixTree {
return &AdaptiveRadixTree{
tree: art.New(),
lock: new(sync.RWMutex),
}
}
func NewMemTable(typ MemTableType) MemTable {
switch typ {
case Btree:
return NewBTree()
case ART:
return NewAdaptiveRadixTree()
default:
return NewBTree()
}
}
func main() {
memTable := NewMemTable(Btree)
memTable.Put([]byte("key"), []byte("value"))
}
1. 工厂方法模式
工厂方法模式(Factory Method Pattern)
工厂方法模式也被称为虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)
简单工厂模式虽然简单,但存在一个很严重的问题,违背了开闭原则。工厂方法模式,继承了简单工厂模式的优点,同时做出了修改以达到符合开闭原则的要求。在工厂方法模式中,不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。
在很多场合有其实际应用,这种模式主要用于创建复杂对象,创建对象的逻辑可能包含一些业务需要的约束。
假设我们正在开发一个日志记录系统,这个系统可以将日志记录到不同的地方,例如,控制台,文件,或者是远程服务器。我们可以使用工厂方法模式来创建适合不同场景的日志记录器。
// Logger 是所有日志记录器的接口
type Logger interface {
Log(message string)
}
// ConsoleLogger 是 Logger 的一个实现,它将日志记录到控制台
type ConsoleLogger struct{}
func (l ConsoleLogger) Log(message string) {
fmt.Println("Console logger: ", message)
}
// FileLogger 是 Logger 的一个实现,它将日志记录到文件
type FileLogger struct{}
func (l FileLogger) Log(message string) {
fmt.Println("File logger: ", message)
}
// LoggerFactory 是所有日志记录器工厂的接口
type LoggerFactory interface {
CreateLogger() Logger
}
// ConsoleLoggerFactory 是 LoggerFactory 的一个实现,它创建 ConsoleLogger
type ConsoleLoggerFactory struct{}
func (f ConsoleLoggerFactory) CreateLogger() Logger {
return ConsoleLogger{}
}
// FileLoggerFactory 是 LoggerFactory 的一个实现,它创建 FileLogger
type FileLoggerFactory struct{}
func (f FileLoggerFactory) CreateLogger() Logger {
return FileLogger{}
}
func main() {
var factory LoggerFactory
factory = ConsoleLoggerFactory{}
logger := factory.CreateLogger()
logger.Log("This is a message")
factory = FileLoggerFactory{}
logger = factory.CreateLogger()
logger.Log("This is another message")
}
2. 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式是一种创建型设计模式,它提供了一种在不指定具体类的情况下创建一系列相关或相互依赖的对象的接口。抽象工厂允许你根据需求切换不同的具体工厂实现,以便在运行时根据配置或其他条件创建适当的对象。
简单来说,抽象工厂模式就是将多个工厂类的接口进行抽象,然后再用一个工厂类来封装这些工厂类的接口。在创建具体对象时,我们通常需要使用其他对象或者数据结构,因此我们还需要定义一些相关的产品接口和产品结构体。
// User 表示用户实体
type User struct {
ID int
Name string
}
// UserDAO 定义了操作用户的接口
type UserDAO interface {
GetUser(id int) (*User, error)
SaveUser(*User) error
DeleteUser(id int) error
}
// DBFactory 定义了抽象工厂接口
type DBFactory interface {
CreateUserDAO() UserDAO
}
// MysqlUserDAO 实现了UserDAO接口,提供了对MySQL数据库的操作
type MysqlUserDAO struct{}
func (dao *MysqlUserDAO) GetUser(id int) (*User, error) {
// 实现从MySQL数据库获取用户的具体操作
// ...
fmt.Println("Get user from MySQL database")
return &User{}, nil
}
func (dao *MysqlUserDAO) SaveUser(u *User) error {
// 实现保存用户到MySQL数据库的具体操作
// ...
fmt.Println("Save user to MySQL database")
return nil
}
func (dao *MysqlUserDAO) DeleteUser(id int) error {
// 实现从MySQL数据库删除用户的具体操作
// ...
fmt.Println("Delete user from MySQL database")
return nil
}
// MysqlFactory 实现了DBFactory接口,用于创建MysqlUserDAO
type MysqlFactory struct{}
func (f *MysqlFactory) CreateUserDAO() UserDAO {
return &MysqlUserDAO{}
}
// MongoUserDAO 实现了UserDAO接口,提供了对MongoDB数据库的操作
type MongoUserDAO struct{}
func (dao *MongoUserDAO) GetUser(id int) (*User, error) {
// 实现从MongoDB数据库获取用户的具体操作
// ...
fmt.Println("Get user from MongoDB database")
return &User{}, nil
}
func (dao *MongoUserDAO) SaveUser(u *User) error {
// 实现保存用户到MongoDB数据库的具体操作
// ...
fmt.Println("Save user to MongoDB database")
return nil
}
func (dao *MongoUserDAO) DeleteUser(id int) error {
// 实现从MongoDB数据库删除用户的具体操作
// ...
fmt.Println("Delete user from MongoDB database")
return nil
}
// MongoFactory 实现了DBFactory接口,用于创建MongoUserDAO
type MongoFactory struct{}
func (f *MongoFactory) CreateUserDAO() UserDAO {
return &MongoUserDAO{}
}
func main() {
mysqlFactory := &MysqlFactory{}
mongoFactory := &MongoFactory{}
userDAO := mysqlFactory.CreateUserDAO()
userDAO.GetUser(1)
userDAO.SaveUser(&User{})
userDAO.DeleteUser(1)
userDAO = mongoFactory.CreateUserDAO()
userDAO.GetUser(1)
userDAO.SaveUser(&User{})
userDAO.DeleteUser(1)
}
工厂方法模式和抽象工厂模式都属于创建型设计模式,它们都是用来创建对象的。但是,两者有一些重要的区别:
工厂方法模式(Factory Method Pattern):
- 工厂方法模式中的每个工厂只创建一种产品。
- 工厂方法模式主要用于创建一种类型的对象,但是这个对象的构造过程可能比较复杂。
- 在工厂方法模式中,客户端通常只需要知道具体工厂的接口,而不需要知道具体工厂类的类型。具体工厂类的类型可以在运行时通过配置或者其他方式决定。
- 例如,在上面的日志记录器的例子中,我们有一个日志记录器的工厂接口和多个实现这个接口的工厂类,每个工厂类都用于创建一种特定的日志记录器。
抽象工厂模式(Abstract Factory Pattern):
- 抽象工厂模式中的每个工厂可以创建多种类型的产品。
- 抽象工厂模式主要用于创建相关的或者是依赖的对象组。对象组中的每个对象都是一个产品,但是这些产品需要一起工作以完成一些更大的功能。
- 在抽象工厂模式中,客户端通常只需要知道具体工厂的接口,而不需要知道具体工厂类的类型。具体工厂类的类型可以在运行时通过配置或者其他方式决定。
- 例如,在上面的数据库访问的例子中,我们有一个数据库访问对象的工厂接口和多个实现这个接口的工厂类,每个工厂类都用于创建一组相关的数据库访问对象,如用户数据访问对象,订单数据访问对象等。
总的来说,如果你只需要创建一种类型的产品,那么使用工厂方法模式可能更简单;如果你需要创建多种类型的相关产品,那么使用抽象工厂模式可能更合适。
3. 建造者模式
建造者模式(Builder Pattern)
建造者模式是一种对象创建型模式,它可以将复杂对象的构建过程与表示分离开来,使得相同的构建过程可以创建不同的表示。
简单来说,建造者模式就是将一个复杂对象的创建过程基于链式调用封装起来,使得这个过程可以有不同的表示方式。
// RequestBuilder 是网络请求的建造者接口
type RequestBuilder interface {
SetURL(url string) RequestBuilder
SetMethod(method string) RequestBuilder
SetHeader(key, value string) RequestBuilder
SetBody(body string) RequestBuilder
Build() *Request
}
// Request 是网络请求对象
type Request struct {
URL string
Method string
Headers map[string]string
Body string
}
// HTTPRequestBuilder 是 HTTP 请求的具体建造者
type HTTPRequestBuilder struct {
request *Request
}
// SetURL 设置请求的 URL
func (b *HTTPRequestBuilder) SetURL(url string) RequestBuilder {
b.request.URL = url
return b
}
// SetMethod 设置请求的方法
func (b *HTTPRequestBuilder) SetMethod(method string) RequestBuilder {
b.request.Method = method
return b
}
// SetHeader 设置请求的头信息
func (b *HTTPRequestBuilder) SetHeader(key, value string) RequestBuilder {
b.request.Headers[key] = value
return b
}
// SetBody 设置请求的消息体
func (b *HTTPRequestBuilder) SetBody(body string) RequestBuilder {
b.request.Body = body
return b
}
// Build 构建网络请求对象
func (b *HTTPRequestBuilder) Build() *Request {
return b.request
}
// NewHTTPRequestBuilder 创建 HTTP 请求的建造者
func NewHTTPRequestBuilder() RequestBuilder {
return &HTTPRequestBuilder{
request: &Request{
Headers: make(map[string]string),
},
}
}
func main() {
// 创建一个 HTTP 请求的建造者
builder := NewHTTPRequestBuilder()
// 使用建造者设置请求的属性
request := builder.
SetURL("https://api.example.com").
SetMethod("GET").
SetHeader("Authorization", "Bearer token").
SetBody("data").
Build()
// 打印请求对象
fmt.Println(request)
}
4. 原型模式
原型模式(Prototype Pattern)
原型模式是一种创建型设计模式,它允许通过克隆现有对象来创建新对象,而无需通过实例化和配置新对象来完成。在原型模式中,我们创建一个原型对象,然后通过复制该对象来创建新的对象,而不是使用常规的构造函数和初始化流程。
应该注意原型模式不是用来获得性能优势的。它仅用于从原型实例创建新对象!
原型模式的实现需要满足以下要素:
定义一个原型对象,该对象实现了 Clone() 方法,用于克隆自身并返回新的克隆对象。
定义一个工厂函数或方法,用于创建原型对象并初始化其属性。
客户端代码可以通过调用原型对象的 Clone() 方法来创建新的对象,而不是直接调用构造函数或工厂函数。
// HTTPRequest 是HTTP请求的原型对象
type HTTPRequest struct {
Method string
URL string
Headers map[string]string
Body []byte
}
// Clone 通过复制原型对象创建新的HTTP请求对象
func (r *HTTPRequest) Clone() *HTTPRequest {
clone := *r
return &clone
}
// HTTPRequestPrototype 是HTTP请求的原型管理器
type HTTPRequestPrototype struct {
prototypes map[string]*HTTPRequest
}
// Register 注册HTTP请求原型对象
func (p *HTTPRequestPrototype) Register(name string, request *HTTPRequest) {
p.prototypes[name] = request
}
// Retrieve 根据名称从原型管理器中检索HTTP请求原型对象
func (p *HTTPRequestPrototype) Retrieve(name string) *HTTPRequest {
prototype, ok := p.prototypes[name]
if !ok {
return nil
}
return prototype.Clone()
}
func main() {
// 创建HTTP请求原型管理器
prototypeManager := &HTTPRequestPrototype{
prototypes: make(map[string]*HTTPRequest),
}
// 注册不同的HTTP请求原型对象
prototypeManager.Register("service1", &HTTPRequest{
Method: "GET",
URL: "http://service1.example.com",
Headers: map[string]string{
"Content-Type": "application/json",
},
})
prototypeManager.Register("service2", &HTTPRequest{
Method: "POST",
URL: "http://service2.example.com",
Headers: map[string]string{
"Content-Type": "application/xml",
},
Body: []byte(`<data>Hello, Service 2</data>`),
})
// 启动中介服务
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 根据请求路径选择对应的HTTP请求原型对象
name := r.URL.Path[1:] // 从路径中获取名称,例如 "/service1" 将匹配到 "service1"
requestPrototype := prototypeManager.Retrieve(name)
if requestPrototype == nil {
http.NotFound(w, r)
return
}
// 复制原型对象,并根据实际请求进行自定义修改
request := requestPrototype.Clone()
request.URL = fmt.Sprintf("%s%s", request.URL, r.URL.Path)
// 发送请求并获取响应
client := &http.Client{}
httpRequest, err := http.NewRequest(request.Method, request.URL, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 设置请求标头
for key, value := range request.Headers {
httpRequest.Header.Set(key, value)
}
// 发送请求
response, err := client.Do(httpRequest)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer response.Body.Close()
// 读取响应内容
body, err := ioutil.ReadAll(response.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 将响应内容写入到HTTP响应中
w.Header().Set("Content-Type", response.Header.Get("Content-Type"))
w.Write(body)
})
// 启动HTTP服务器,监听端口
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("Failed to start server:", err)
}
}
5. 单例模式
单例模式(Singleton Pattern)
顾名思义,指的就是一个类只能生成一个实例,且该类能自行创建这个实例的一种模式。
在服务的生命周期内,一个类生成的一个实例对象只能存在一个,调用时复用该单例对象即可,这样既节省了内存空间,也节省了创建新对象时的资源消耗,也便于资源管理。在日常开发中,很多场景其实都可以被设计成单例,像线程池、全局日志、数据库等等,单例模式总结下来有几个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点
单例模式一般有两种实现方式
饿汉式(Eager Initialization):
-
在程序启动时或单例类被加载时,就立即创建并初始化单例实例。
-
单例实例在整个程序生命周期中都存在,无论是否使用。
-
线程安全,因为在实例创建时就完成了初始化。
-
示例代码:
type singleton struct { } var _instance *singleton func init() { _instance = &singleton{} } func GetInstance() *singleton { return _instance }
懒汉式(Lazy Initialization):
-
在第一次调用获取单例实例的方法时,才创建并初始化单例实例。
-
单例实例是按需创建的,可能存在多个线程同时请求获取单例实例的情况。
-
需要考虑线程安全性,通常使用锁或其他同步机制来保证只有一个实例被创建。
-
示例代码:
type singleton struct { } var _instance *singleton var once sync.Once // GetInstance lazy func GetInstance() *singleton { once.Do(func() { _instance = &singleton{} }) return _instance }
extra 配置模式
选项模式(Options Pattern)是一种常用的设计模式,得益于对函数式编程的支持,选项模式Go语言中经常使用。选项模式用于在函数或结构体中设置多个可选参数,以便在调用时提供更灵活的配置选项。
type Options struct {
Path string
MaxSize int
Sync bool
}
// DefaultOptions 返回默认配置选项
func DefaultOptions() *Options {
return _defaultConf
}
var _defaultConf = &Options{
Path: "/temp/opt",
MaxSize: 100,
Sync: true,
}
// NewOptions 处理配置选项
func NewOptions(opts ...func(*Options)) *Options {
// 获取默认配置选项
options := _defaultConf
// 遍历传入的选项函数列表,逐个应用选项函数
for _, opt := range opts {
opt(options)
}
return options
}
func WithPath(value string) func(*Options) {
return func(opts *Options) {
opts.Path = value
}
}
func WithMaxSize(value int) func(*Options) {
return func(opts *Options) {
opts.MaxSize = value
}
}
func WithSync(value bool) func(*Options) {
return func(opts *Options) {
opts.Sync = value
}
}
func main() {
// 使用选项模式调用函数
opt := NewOptions(
WithPath("custom value"),
WithMaxSize(200),
WithSync(false),
)
fmt.Println(opt)
}