设计模式-创建型-go实现版

卡二条的技术圈

共 8987字,需浏览 18分钟

 ·

2023-03-03 18:55

介绍

在软件开发中,创建型设计模式(Creational Design Pattern)是一组用于对象创建的设计模式。它们的共同目标是封装对象的创建过程,从而提高代码的复用性和灵活性。根据经典的GOF设计模式,创建型模式主要包括以下5种:
  1. 工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法模式让一个类的实例化延迟到其子类中进行。

  2. 抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

  3. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。

  4. 建造者模式(Builder Pattern):将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

  5. 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并通过复制这些原型来创建新的对象。

除了这五种经典的创建型设计模式,还有一些变体或衍生的创建型模式,例如简单工厂模式、多例模式等。每种创建型模式都有其特定的适用场景和优缺点,开发者需要根据实际情况选择最适合自己项目需求的模式。

使用场景

创建型设计模式是用于创建对象的一种模式。常见的创建型设计模式有以下几种:
  1. 工厂方法模式:定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个,适用于需要动态地创建对象的场景。

  2. 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,适用于需要创建一系列相关对象的场景。

  3. 单例模式:确保一个类只有一个实例,并提供全局访问点,适用于需要确保只有一个实例存在的场景。

  4. 原型模式:通过复制已有对象来创建新对象,而无需知道任何创建的细节,适用于需要快速创建新对象的场景。

  5. 建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,适用于需要创建复杂对象的场景。

在选择创建型设计模式时,需要考虑以下几个方面:
  1. 系统的复杂性:如果系统非常复杂,那么可能需要使用创建型设计模式来帮助管理对象的创建和组织。

  2. 对象的数量:如果需要创建大量的对象,那么可以考虑使用创建型设计模式来减少对象的创建和销毁次数。

  3. 对象的依赖关系:如果对象之间存在复杂的依赖关系,那么可以考虑使用创建型设计模式来处理这些依赖关系。

  4. 系统的扩展性:选择合适的创建型设计模式可以使系统更易于扩展和维护。

总之,在选择创建型设计模式时,需要结合自己的经验和技能,综合考虑各种因素,以选择最适合自己的设计模式。同时,还需要注意不要过度使用设计模式,以免代码过于复杂。

实现例子

工厂方法模式

工厂方法模式是一种常用的创建型设计模式,它的核心思想是将对象的创建过程封装起来,使得对象的创建和使用相分离。在 Go 语言中,可以通过接口和结构体实现工厂方法模式。
package main

import "fmt"

// 定义接口
type Product interface {
    GetName() string
}

// 定义结构体A
type ProductA struct{
 AxxProti AAA
}

func (p *ProductA) GetName() string {
    return "ProductA"
}

// 定义结构体B
type ProductB struct{}

func (p *ProductB) GetName() string {
    return "ProductB"
}

// 定义工厂接口
type Factory interface {
    Create() Product
}

// 定义工厂A
type FactoryA struct{}

func (f *FactoryA) Create() Product {
    return &ProductA{}
}

// 定义工厂B
type FactoryB struct{}

func (f *FactoryB) Create() Product {
    return &ProductB{}
}

func main() {
    // 创建工厂A
    factoryA := &FactoryA{}
    // 通过工厂A创建产品A
    productA := factoryA.Create()
    fmt.Println(productA.GetName()) // 输出:ProductA

    // 创建工厂B
    factoryB := &FactoryB{}
    // 通过工厂B创建产品B
    productB := factoryB.Create()
    fmt.Println(productB.GetName()) // 输出:ProductB
}
在这个示例中,Product 接口定义了产品的方法,ProductAProductB 结构体分别实现了 Product 接口。Factory 接口定义了工厂的方法,FactoryAFactoryB 结构体分别实现了 Factory 接口,其中 FactoryA 可以用于创建 ProductAFactoryB 可以用于创建 ProductB
main 函数中,我们创建了 FactoryAFactoryB 两个工厂,然后通过它们分别创建了 ProductAProductB 两个产品。由于每个工厂只能创建对应的产品,因此输出的结果也不同。

抽象工厂模式

抽象工厂模式是一种创建型设计模式,它提供了一个创建一系列相关或依赖对象的接口,而无需指定具体类。在 Go 语言中,可以通过接口和结构体实现抽象工厂模式。
package main

import "fmt"

// 定义接口1
type Button interface {
    Paint()
}

// 定义接口2
type Label interface {
    Paint()
}

// 定义工厂接口
type GUIFactory interface {
    CreateButton() Button
    CreateLabel() Label
}

// 定义具体按钮结构体
type WindowsButton struct{}

func (b *WindowsButton) Paint() {
    fmt.Println("Windows Button")
}

type MacButton struct{}

func (b *MacButton) Paint() {
    fmt.Println("Mac Button")
}

// 定义具体标签结构体
type WindowsLabel struct{}

func (l *WindowsLabel) Paint() {
    fmt.Println("Windows Label")
}

type MacLabel struct{}

func (l *MacLabel) Paint() {
    fmt.Println("Mac Label")
}

// 定义具体工厂
type WindowsGUIFactory struct{}

func (f *WindowsGUIFactory) CreateButton() Button {
    return &WindowsButton{}
}

func (f *WindowsGUIFactory) CreateLabel() Label {
    return &WindowsLabel{}
}

type MacGUIFactory struct{}

func (f *MacGUIFactory) CreateButton() Button {
    return &MacButton{}
}

func (f *MacGUIFactory) CreateLabel() Label {
    return &MacLabel{}
}

func main() {
    // 创建Windows GUI工厂
    windowsFactory := &WindowsGUIFactory{}
    windowsButton := windowsFactory.CreateButton()
    windowsLabel := windowsFactory.CreateLabel()

    windowsButton.Paint() // 输出:Windows Button
    windowsLabel.Paint()  // 输出:Windows Label

    // 创建Mac GUI工厂
    macFactory := &MacGUIFactory{}
    macButton := macFactory.CreateButton()
    macLabel := macFactory.CreateLabel()

    macButton.Paint() // 输出:Mac Button
    macLabel.Paint()  // 输出:Mac Label
}

单例模式

在 Go 语言中,可以通过 sync 包的 Once 类型实现单例模式。Once 类型可以保证其内部的函数只会执行一次,因此我们可以利用它来实现单例模式。
package singleton

import "sync"

type singleton struct{}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}
在上面的代码中,我们首先定义了一个名为 singleton 的结构体类型,用来表示单例对象。然后定义了一个全局变量 instance,用来存储单例对象的实例。同时,我们还定义了一个 sync.Once 类型的全局变量 once,用来保证 GetInstance 函数只会执行一次。
GetInstance 函数会首先判断 instance 是否已经被初始化。如果已经被初始化,直接返回 instance;否则,执行 once.Do 函数来初始化 instance,并返回 instance。
使用 sync.Once 实现单例模式的好处在于它可以保证线程安全,并且只会在第一次使用时才会初始化单例对象。这样可以避免在程序启动时就初始化单例对象,从而提高程序启动速度。

建造者模式

在 Go 语言中,可以通过建造者模式来创建复杂的对象。建造者模式将对象的创建过程分解为多个简单的步骤,从而使得不同的创建过程可以组合在一起,生成不同的对象。
package builder

type Builder interface {
    SetName(name string) Builder
    SetAge(age int) Builder
    Build() *Person
}

type Person struct {
    Name string
    Age  int
}

type PersonBuilder struct {
    person *Person
}

func NewPersonBuilder() *PersonBuilder {
    return &PersonBuilder{person: &Person{}}
}

func (b *PersonBuilder) SetName(name string) Builder {
    b.person.Name = name
    return b
}

func (b *PersonBuilder) SetAge(age int) Builder {
    b.person.Age = age
     return b
}

func (b *PersonBuilder) Build() *Person {
    return b.person
}

在上面的代码中,我们定义了一个 Builder 接口,用来描述建造者模式的创建过程。Builder 接口中包含了多个简单的方法,用来设置不同的属性。同时,我们还定义了一个 Person 结构体,用来表示最终生成的对象。
PersonBuilder 结构体实现了 Builder 接口,并包含了一个指向 Person 结构体的指针。在 SetName 和 SetAge 方法中,我们设置了 Person 结构体的 Name 和 Age 属性,并返回了 PersonBuilder 的指针,用于链式调用。在 Build 方法中,我们返回了 Person 结构体的指针,表示创建过程结束。
使用建造者模式可以将复杂对象的创建过程分解为多个简单的步骤,从而使得创建过程更加灵活和可扩展。在上面的示例中,我们只需要在 PersonBuilder 结构体中添加更多的方法来设置其他属性,就可以创建不同的 Person 对象。

原型模式

原型模式是一种创建型设计模式,其主要思想是通过克隆现有对象来创建新的对象,从而避免使用昂贵的构造函数。在 Go 语言中,可以通过接口和结构体来实现原型模式。
package prototype

import (
    "fmt"
)

type Prototype interface {
    Clone() Prototype
    GetName() string
}

type ConcretePrototype struct {
    name string
}

func (p *ConcretePrototype) Clone() Prototype {
    return &ConcretePrototype{name: p.name}
}

func (p *ConcretePrototype) GetName() string {
    return p.name
}

func main() {
    prototype := &ConcretePrototype{name: "prototype"}
    clone := prototype.Clone()
    fmt.Println(clone.GetName())
}

在上面的代码中,我们定义了一个 Prototype 接口,其中包含了 Clone 和 GetName 两个方法。Clone 方法用于克隆对象,GetName 方法用于获取对象的名称。同时,我们还定义了一个 ConcretePrototype 结构体,用于表示具体的原型对象。在 Clone 方法中,我们通过创建一个新的 ConcretePrototype 对象来克隆原型对象。在 GetName 方法中,我们返回了原型对象的名称。
在 main 函数中,我们首先创建了一个 ConcretePrototype 对象,然后通过调用 Clone 方法来克隆该对象。最后,我们打印了克隆对象的名称,证明克隆操作成功。
使用原型模式可以避免使用昂贵的构造函数,从而提高对象创建的效率。另外,原型模式还可以用来动态生成对象,具有很好的扩展性。



浏览 17
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报