关于设计模式:Go应用设计模式之创建型

2次阅读

共计 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 Trans­porter interface {Deliv­er()
}

type Truck struct {}

func (J Truck) Deliv­er() {fmt.Println("this is Truck Deliv­er")
}

type Ship struct {}


func (Y Ship) Deliv­er() {fmt.Println("this is Ship Deliv­er")
}


func NewTrans­port(t string) Trans­porter {
    switch t {
    case "truck":
        return Truck{}
    case "ship":
        return Ship{}}
    return nil
}

只管简略工厂模式的代码实现中,有多处 if 分支判断逻辑,违反开闭准则,但衡量扩展性和可读性,这样的代码实现在大多数状况下 (无需频繁的增加类,也没有太多的类) 是没有问题的。

三、工厂办法(Factory Method)

定义:

简略工厂应用惟一的一个工厂管制着所有产品的实例化,而 工厂办法 中包含一个工厂接口,咱们能够动静的实现多种工厂,达到扩大的目标。
工厂办法模式比起简略工厂模式更加合乎开闭准则,当咱们新增一品种 的时候,只须要新增一个实现了接口的类即可。

应用场景:

当对象的创立逻辑比较复杂,不只是简略的 new 一下就能够,而是要组合其余类对象,做各种初始化操作的时候,咱们举荐应用工厂办法模式,将简单的创立逻辑拆分到多个工厂类中,让每个工厂类都不至于过于简单。

实现

还是下面的例子,如果公司开了分公司,在北京和广州都有了物流基地,运输路径的中央必定会不一样,又将如何实现呢?

package factory

// 工厂接口
type CompanyInterface interface {Generate(t string) Trans­porter
}

// 产品接口
type Trans­porter interface {Deliv­er()
}

// 具体工厂类
type BjCompany struct {}

type GZCompany  struct {}


// 工厂办法
func (bj BjCompany) Generate(t string) Trans­porter {
    switch t {
    case "truck":
        return Truck{}
    case "ship":
        return Ship{}}
    return nil
}

func (gz GZCompany) Generate(t string) Trans­porter {
    switch t {
    case "truck":
        return Truck{}
    case "ship":
        return Ship{}}
    return nil
}

// 具体产品及办法实现
type BJTruck struct {}

func (b BJTruck) Deliv­er() {fmt.Println("this is Truck Deliv­er")
}

type BJShip struct {}

func (b BJShip) Deliv­er() {fmt.Println("this is Ship Deliv­er")
}

type GZTruck struct {}

func (g GZTruck) Deliv­er() {fmt.Println("this is Truck Deliv­er")
}

type GZShip struct {}

func (g GZShip) Deliv­er() {fmt.Println("this is Ship Deliv­er")
}

简略工厂模式和工厂办法模式看起来很类似,本质区别就在于,如果在物流公司中间接创立运输产品,是依赖具体某个分公司的,扩展性、弹性、可维护性都较差,而如果将实例化的代码形象进去,不再依赖具体的公司,而是依赖于形象的公司接口,使对象的实现从应用中解耦,这样就领有很强的扩展性了,也能够称为『依赖倒置准则』

四、形象工厂(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…

正文完
 0