1.简介

创建型设计模式抽象了实例化的过程。它们帮助一个系统独立于如何创建、组合和标是它们的那些对象。

对于Golang来说,完全摒弃了类继承,用对象组合取而代之,这让创建型模型变得更加重要。

2.抽象工厂(Abstract Factory)

2.1 意图

提供一个接口以创建一系列相关或相互依赖的对象,而无须指定他们具体的类。

2.2 适用性

  • 一个系统要独立于它的产品的创建、组合和表示
  • 提供一个产品类库,即一个接口而不是实现

3.3 优缺点

优点:

  • 分离了具体的类
  • 通过接口的限制,利于产品的一致性 缺点:
  • 难以支持新种类的产品

3.4 实现

假设移动设备生产商通常由手机生产线和手机壳生产线组成,我们使用Moblie接口来定义一个生产商,到这里抽象工厂就完成了。这个接口统一了所有生产商的行为。致于具体实现这个接口的工厂,可以用工厂方法,生成器等方法实现。

package abstract
//此接口即抽象工厂
type Mobile interface {
    CreatePhone(phoneType string) Phone  //生产手机
    CreateShell(shellShape string) Shell //生产手机壳
}

//
type Phone interface {
    Show()
}

type Shell interface {
    Show()
}

3.生成器(Builder)

3.1 意图

将一个复杂对象的初始化与它的表示分离,使得同样的构建过程可以创建不通同的表示。

3.2 适用性

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
  • 当构造过程必须允许被构造的对象有不同的表示时

3.3 效果

  • 可以改表一个产品的内部表示
  • 将构造(初始化)代码和表示代码分开
  • 使你可以对构造(初始化)过程更精细的控制

3.4 实现

假设我们有一个生产华子手机的生成器如下:

package builder

type HuaZiBuilder struct {
    feat string
}

func NewBuilder(feats ...string) *HuaZiBuilder {
    var feat string
    for _, f := range feats {
        feat += " " + f
    }
    return &HuaZiBuilder{feat: feat}
}

func (builder *HuaZiBuilder) SetCpu(cpu string) *HuaZiBuilder {
    builder.feat += " " + cpu
    return builder
}

func (builder *HuaZiBuilder) SetGpu(gpu string) *HuaZiBuilder {
    builder.feat += " " + gpu
    return builder
}

func (builder *HuaZiBuilder) SetSim(sim string) *HuaZiBuilder {
    builder.feat += " " + sim
    return builder
}

我们可以通过华子的NewBuilder方法一次设置Cpu、Gpu、Sim等所有属性,但也可以通过SetCpu,SetGpu等方法精细化的控制内部属性:

//1
huazi := NewBuilder("ji", "ni", "tai", "mei")
//2
huazi := NewBuilder().SetCpu("ji").SetGpu("3080").SetSim("移动")

builder的实现则是这几个SetXxx函数,每个Set函数设置内部属性后都返回当前对象。

4.工厂方法

4.1 意图

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

4.2 适用性

  • 当一个类或一个代码段内不能确定它必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。

4.3 实现

我们延用抽象工厂的接口来实现我们具体的工厂类及方法,我们这里实现一个生产安卓的工厂类:

package factory

import (
    abstract "creater/abstract_factory"
    "fmt"
)

var _ abstract.Mobile = &AndroidFactory{}

type AndroidFactory struct {
}

func NewAndroidFactory() *AndroidFactory {
    return &AndroidFactory {}
}

func (a *AndroidFactory) CreatePhone(phoneType string) abstract.Phone {
    switch phoneType {
    case "Huazi":
        return &HuaZi{}
    case "Honer":
        return &Honer{}
    default:
        return nil
    }
}

func (a *AndroidFactory) CreateShell(shellShape string) abstract.Shell {
    switch shellShape {
    default:
        return &NormalShell{}
    }
}

//
type HuaZi struct {
}

func (h *HuaZi) Show() {
    fmt.Println("这里是中国!v me 50!")
}

//
type Honer struct {
}

func (h *Honer) Show() {
    fmt.Println("这里是中国的荣耀!")
}

CreatePhone方法会根据传入的参数来决定生产Honer手机还是Huazi手机,同样的CreateShell也是如此,通过工厂方法可以将类的创建变得更灵活。

huazi := NewAndroidFactory().CreatePhone("huazi")
honer := NewAndroidFactory().CreatePhone("honer")


5.原型(prototype)

5.1 意图

用原型实例指定创建对象的种类,并拷贝其属性创建一个新对象。

5.2 适用性

当需要的一个类已经具有多种属性且不便于从头初始化时。

这个在我们的xml等流程图软件中很常见,例如你已经编辑了一个相当复杂的流程图组件,你复制时当然是希望带上其所有属性,而不是从三角形开始从新构造编译。

5.3 实现

在builder中,我们的HuaZi可谓是有相当多的属性,这时我们希望快速生成一个带有相同属性的新类,则可添加如下的Clone函数,用于快速克隆生成新对象。其实在Java中Clone函数是Object类函数,所有的对象都可实现此类。对于原型来说,就已经完成了。

func (builder *HuaZiBuilder) Clone() *HuaZiBuilder {
    newHuaZi := &HuaZiBuilder{feat: builder.feat}
    return newHuaZi
}

huazi := NewBuilder().SetCpu("ji").SetGpu("3080").SetSim("移动")
//复制一个一样属性的华子
newHuazi := huazi.Clone()

6.单例(singleton)

6.1 意图

保证一个类仅有一个实离,并提供一个访问它的全局访问点

6.2 适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

6.3 实现

这里创建一个全局唯一的HuaZi生成器,由于单例非常常见,就不做过多的解释了。

package singleton

import (
    builder "creater/builder_prototype"
    "sync"
)

var once sync.Once
var huazi *builder.HuaZiBuilder

func GetHuaZiInstance() *builder.HuaZiBuilder {
    once.Do(func() {
        huazi = builder.NewBuilder("4G", "+", "HM", ">", "5G")
    })
    return huazi
}