go语言设计模式 - 工厂模式 | 青训营笔记

56 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天

go语言设计模式 - 工厂模式

1. 为什么需要工厂模式

如果不使用工厂模式,代码如下:

package main
​
import "fmt"// 水果类
type Fruit struct {
}
​
// 创建一个Fruit类
func NewFruit(name string) *Fruit {
    fruit := new(Fruit)
​
    switch name {
    case "apple":
        // 创建Apple逻辑
    case "banana":
        // 创建banana逻辑
    case "pear":
        // 创建pear逻辑
    }
    return fruit
}
​
func (f *Fruit) Show(name string) {
    switch name {
    case "apple":
        fmt.Println("我是苹果")
    case "banana":
        fmt.Println("我是香蕉")
    case "pear":
        fmt.Println("我是梨子")
    default:
        fmt.Println("无匹配项")
    }
}
​
// 业务逻辑
func main() {
    apple := NewFruit("apple")
    apple.Show("apple")
}
​

在上述代码中,存在以下几个问题:

  1. 在NewFruit函数中有很多的 switch... case... 语句,整个函数的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。
  2. Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水果对象的初始化代码和显示代码集中在一个类中实现,违反了”单一职责原则“,不利于类的重用和维护。
  3. 当需要新增新类型的水果时,必须修改Fruit类的构造函数NewFruit()和其他相关方法源代码,违反了”开闭原则“。

2. 简单工厂模式

image-20230119172109475

用户只需要接触到Factory(工厂类)和水果类就好,其他具体实现的类不需要考虑。

package main
​
import "fmt"// 抽象层
type Fruit interface {
    Show()
}
​
// 实现层
type Apple struct {
    Fruit // 为了便于理解
}
​
func (apple *Apple) Show() {
    fmt.Println("I am Apple")
}
​
type Banana struct {
    Fruit
}
​
func (banana *Banana) Show() {
    fmt.Println("I am Banana")
}
​
type Pear struct {
}
​
func (pear *Pear) Show() {
    fmt.Println("I am Pear")
}
​
// 工厂模块
type Factory struct {
}
​
func (fac *Factory) CreateFruit(kind string) Fruit {
    var fruit Fruit
    switch kind {
    case "apple":
        fruit = new(Apple)
    case "banana":
        fruit = new(Banana)
    case "pear":
        fruit = new(Pear)
    default:
        fmt.Println("无匹配项")
    }
    return fruit
}
​
func main() {
    factory := new(Factory)
​
    // apple
    apple := factory.CreateFruit("apple")
    apple.Show()
​
    // banana
    banana := factory.CreateFruit("banana")
    banana.Show()
​
    // pear
    pear := factory.CreateFruit("pear")
    pear.Show()
}

3. 工厂方法模式

简单工厂模式不属于23中设计模式之一,如果我们还需要一个桃子类,就需要在CreateFruit()中再加一条case,违背了开闭原则。

于是便有了工厂方法模式。

image-20230119174149901

代码如下:

package main
​
import "fmt"// ============== 抽象层 ===============
// 水果类 抽象的接口
type Fruit interface {
    Show()
}
​
// 工厂类(抽象的接口)
type AbstractFactory interface {
    CreateFruit() Fruit
}
​
// 实现层
type Apple struct {
    Fruit // 为了便于理解
}
​
func (apple *Apple) Show() {
    fmt.Println("I am Apple")
}
​
type Banana struct {
    Fruit
}
​
func (banana *Banana) Show() {
    fmt.Println("I am Banana")
}
​
type Pear struct {
}
​
func (pear *Pear) Show() {
    fmt.Println("I am Pear")
}
​
// ========== 基础的工厂模块 =================
// 苹果工厂
type AppleFactory struct{
    AbstractFactory
}
​
func (fac *AppleFactory) CreateFruit() Fruit {
    fruit := new(Apple)
    return fruit
}
​
// 香蕉工厂
type BananaFactory struct{
    AbstractFactory
}
​
func (fac *BananaFactory) CreateFruit() Fruit {
    fruit := new(Banana)
    return fruit
}
​
// 梨子工厂
type PearFactory struct{
    AbstractFactory
}
​
func (fac *PearFactory) CreateFruit() Fruit {
    fruit := new(Pear)
    return fruit
}
​
​
func main() {
    // 需求 需要一个具体的苹果对象
    var appleFac AbstractFactory
    appleFac = new(AppleFactory)
​
    var apple Fruit
    apple = appleFac.CreateFruit()
​
    apple.Show()
}

此时我们需要新增加一个桃子类,只需要新增加一个桃子类并实现其对应的方法便可,不需要修改代码,符合开闭原则。

优点:

  1. 不需要记住具体类名,甚至连具体参数都不用记忆
  2. 实现了对象创建和使用的分离
  3. 系统的可扩展性也就变得非常好,无需修改接口和原类
  4. 对于新产品的创建,符合开闭原则

缺点:

  1. 增加系统中类的个数,复杂度和理解度增加
  2. 增加了系统的抽象性和理解难度

适用场景:

  1. 客户端不知道它所需要的对象的类。
  2. 抽象工厂类通过其子类来指定创建哪个对象

我的博客地址