共计 5555 个字符,预计需要花费 14 分钟才能阅读完成。
创立型模式提供了创建对象的机制,可能晋升已有代码的灵活性和可复用性。上面别离来说一下常见的几种创立性设计模式:
一、单例模式
定义:
一个类只容许创立一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例模式。
应用场景:
从业务概念上,有些数据在零碎中只应该保留一份,就比拟适宜设计为单例类。比方,零碎的配置信息类。除此之外,咱们还能够应用单例解决资源拜访抵触的问题。
实现
概括起来,要实现一个单例,咱们须要关注的点无外乎上面几个:
- 构造函数须要是 private 拜访权限的,这样能力防止内部通过 new 创立实例;
- 思考对象创立时的线程平安问题;
- 思考是否反对提早加载;
- 思考 getInstance() 性能是否高(是否加锁)。
依据是否反对提早加载,单例模式分为饿汉式和懒汉式
1. 饿汉式
在类加载的时候,instance 动态实例就曾经创立并初始化好 了,所以,instance 实例的创立过程是线程平安的。
package singleton
type Singleton struct{}
var singleton *Singleton
func init() {singleton = &Singleton{}
}
// GetInstance 获取实例
func GetInstance() *Singleton {return singleton}
2. 懒汉式(双重检测)
懒汉式绝对于饿汉式的劣势是反对提早加载,然而因为须要加锁,有性能问题。
双重检测:在懒汉式的实现形式中,只有 instance 被创立之后,即使再调用 getInstance() 函数也不会再进入到加锁逻辑中,这样便既反对提早加载、又反对高并发了。
package singleton
import "sync"
var (
lazySingleton *Singleton
once = &sync.Once{})
// GetLazyInstance 懒汉式
func GetLazyInstance() *Singleton {
if lazySingleton == nil {once.Do(func() {lazySingleton = &Singleton{}
})
}
return lazySingleton
}
饿汉式将耗时的初始化操作,提前到程序启动的时候实现,这样就能防止在程序运行的时候,再去初始化导致的性能问题。
如果实例占用资源多,依照 fail-fast 的设计准则(有问题及早裸露),那咱们也心愿在程序启动时就将这个实例初始化好。如果资源不够,咱们能够立刻去修复。
综合来说,饿汉式还是比反对双重检测的懒汉式更举荐应用。
个别状况下,工厂模式分为三种更加细分的类型: 简略工厂、工厂办法和形象工厂。在这三种细分的工厂模式中,简略工厂、工厂办法原理比较简单,在理论的我的项目中也比拟罕用。而形象工厂的原理略微简单点,在理论的我的项目中绝对也不罕用。
二、简略工厂(Simple Factory)
定义
定义一个工厂类,它能够依据参数的不同返回不同类的实例,被创立的实例通常都具备独特的父类。
应用场景
例如,一个物流公司,有陆运、海运和空运几种运输模式,这里的实现如下:申明一个 Transport 接口,它有一个叫 deliver 交付的办法,卡车 Truck 类和轮船 Ship 类都实现该接口,每个类都将以不同的形式实现该办法,卡车走陆路交付货物,轮船走海路交付货物。工厂类能够依据传进的参数实例化相应的类,调用 deliver 办法都能实现运输的工作。
实现
package factory
type Transporter interface {Deliver()
}
type Truck struct {}
func (J Truck) Deliver() {fmt.Println("this is Truck Deliver")
}
type Ship struct {}
func (Y Ship) Deliver() {fmt.Println("this is Ship Deliver")
}
func NewTransport(t string) Transporter {
switch t {
case "truck":
return Truck{}
case "ship":
return Ship{}}
return nil
}
只管简略工厂模式的代码实现中,有多处 if 分支判断逻辑,违反开闭准则,但衡量扩展性和可读性,这样的代码实现在大多数状况下 (无需频繁的增加类,也没有太多的类) 是没有问题的。
三、工厂办法(Factory Method)
定义:
简略工厂应用惟一的一个工厂管制着所有产品的实例化,而 工厂办法 中包含一个工厂接口,咱们能够动静的实现多种工厂,达到扩大的目标。
工厂办法模式比起简略工厂模式更加合乎开闭准则,当咱们新增一品种 的时候,只须要新增一个实现了接口的类即可。
应用场景:
当对象的创立逻辑比较复杂,不只是简略的 new 一下就能够,而是要组合其余类对象,做各种初始化操作的时候,咱们举荐应用工厂办法模式,将简单的创立逻辑拆分到多个工厂类中,让每个工厂类都不至于过于简单。
实现
还是下面的例子,如果公司开了分公司,在北京和广州都有了物流基地,运输路径的中央必定会不一样,又将如何实现呢?
package factory
// 工厂接口
type CompanyInterface interface {Generate(t string) Transporter
}
// 产品接口
type Transporter interface {Deliver()
}
// 具体工厂类
type BjCompany struct {}
type GZCompany struct {}
// 工厂办法
func (bj BjCompany) Generate(t string) Transporter {
switch t {
case "truck":
return Truck{}
case "ship":
return Ship{}}
return nil
}
func (gz GZCompany) Generate(t string) Transporter {
switch t {
case "truck":
return Truck{}
case "ship":
return Ship{}}
return nil
}
// 具体产品及办法实现
type BJTruck struct {}
func (b BJTruck) Deliver() {fmt.Println("this is Truck Deliver")
}
type BJShip struct {}
func (b BJShip) Deliver() {fmt.Println("this is Ship Deliver")
}
type GZTruck struct {}
func (g GZTruck) Deliver() {fmt.Println("this is Truck Deliver")
}
type GZShip struct {}
func (g GZShip) Deliver() {fmt.Println("this is Ship Deliver")
}
简略工厂模式和工厂办法模式看起来很类似,本质区别就在于,如果在物流公司中间接创立运输产品,是依赖具体某个分公司的,扩展性、弹性、可维护性都较差,而如果将实例化的代码形象进去,不再依赖具体的公司,而是依赖于形象的公司接口,使对象的实现从应用中解耦,这样就领有很强的扩展性了,也能够称为『依赖倒置准则』
四、形象工厂(Abstract Factory)
定义
形象工厂模式就是咱们的形象工厂约定了能够生产的产品,这些产品都蕴含多种规格,而后咱们能够从形象工厂为每一种规格派生出具体工厂类,而后让这些具体工厂类生产具体的产品。
咱们能够让一个工厂负责创立多个不同类型的对象,而不是只创立一种 对象。这样就能够无效地缩小工厂类的个数。
应用场景
默认的规定配置解析器类只会依据配置文件格式 (Json、Xml、Yaml……) 来分类,然而,如果类有两种分类方 式,比方,咱们既能够依照配置文件格式来分类,也能够依照解析的对象 (Rule 规定配置还是 System 系统配置) 来分类,那就会这有 8 个 parser 类。
针对这种非凡的场景,如果还是持续用工厂办法来实现的话,咱们要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。
形象工厂就是针对这种十分非凡的场景而诞生的。
package factory
// IRuleConfigParser IRuleConfigParser
type IRuleConfigParser interface {Parse(data []byte)
}
// jsonRuleConfigParser jsonRuleConfigParser
type jsonRuleConfigParser struct{}
// Parse Parse
func (j jsonRuleConfigParser) Parse(data []byte) {panic("implement me")
}
// ISystemConfigParser ISystemConfigParser
type ISystemConfigParser interface {ParseSystem(data []byte)
}
// jsonSystemConfigParser jsonSystemConfigParser
type jsonSystemConfigParser struct{}
// Parse Parse
func (j jsonSystemConfigParser) ParseSystem(data []byte) {panic("implement me")
}
// IConfigParserFactory 工厂办法接口
type IConfigParserFactory interface {CreateRuleParser() IRuleConfigParser
CreateSystemParser() ISystemConfigParser}
type jsonConfigParserFactory struct{}
func (j jsonConfigParserFactory) CreateRuleParser() IRuleConfigParser {return jsonRuleConfigParser{}
}
func (j jsonConfigParserFactory) CreateSystemParser() ISystemConfigParser {return jsonSystemConfigParser{}
}
五、建造者模式
定义:
建造者模式是用来创立一种类型的简单对象,通过设置不同的可选参数,“定制化”地创立不同的对象。
工厂模式是用来创立不同然而相干类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创立哪种类型的对象。
应用场景:
- 参数很多
- 参数蕴含必选参数和可选参数
- 配置项之间有肯定的依赖关系
- 创立的对象是不可变对象
上述的状况间接应用构造函数设置必填项,通过 set() 办法设置可选配置项就无奈满足了
示例
顾客走进一家餐馆点餐,咱们利用工厂模式,依据用户不同的抉择,来制作不同的食物,比方披萨、汉堡、沙拉。
对于披萨来说,用户又有各种配料能够定制,比方奶酪、西红柿、起司,咱们通过建造者模式依据用户抉择的不同配料来制作披萨。
实现
咱们须要定义一个资源池配置类 ResourcePoolConfig。这里的资源池,你能够简略了解为线程池、连接池、对象池等。在这个资源池配置类中,有以下几个成员变量,也就是可配置项。
package builder
import "fmt"
// ResourcePoolConfigOption option
type ResourcePoolConfigOption struct {
maxTotal int
maxIdle int
minIdle int
}
// ResourcePoolConfigOptFunc to set option
type ResourcePoolConfigOptFunc func(option *ResourcePoolConfigOption)
// NewResourcePoolConfig NewResourcePoolConfig
func NewResourcePoolConfig(name string, opts ...ResourcePoolConfigOptFunc) (*ResourcePoolConfig, error) {
if name == "" {return nil, fmt.Errorf("name can not be empty")
}
option := &ResourcePoolConfigOption{
maxTotal: 10,
maxIdle: 9,
minIdle: 1,
}
for _, opt := range opts {opt(option)
}
if option.maxTotal < 0 || option.maxIdle < 0 || option.minIdle < 0 {return nil, fmt.Errorf("args err, option: %v", option)
}
if option.maxTotal < option.maxIdle || option.minIdle > option.maxIdle {return nil, fmt.Errorf("args err, option: %v", option)
}
return &ResourcePoolConfig{
name: name,
maxTotal: option.maxTotal,
maxIdle: option.maxIdle,
minIdle: option.minIdle,
}, nil
}
参考资料:
1、《设计模式之美》
2、https://refactoringguru.cn/de…
3、https://lailin.xyz/post/go-de…