1.干饭的才艺(工厂方法模式)

池鱼rain · · 662 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

工厂方法模式(Factory Method)

包括两种实现方法:

  • 1.简单工厂模式(Simple Factory)
  • 2.工厂方法模式(Factory Method)

1.简单工厂模式

将对象的创建过程,用一个公共的函数封装起来,外部只需要调用该函数即可.很多go源码中都在使用这种方法.当看到NewXXX开头的函数,基本都是这种模式的体现.

啥也不说,上才艺

package simple

import (
    "fmt"
)

type FoodKind int

const (
    MeatKind FoodKind = iota
    FruitKind
    VegetableKind
)

//Food 食物接口
type Food interface {
    Eat()
}

//肉-食物接口的实现
type meat struct {
}

func (t *meat) Eat() {
    fmt.Println("Eat meat")
}

//水果-食物接口的实现
type fruit struct {
}

func (t *fruit) Eat() {
    fmt.Println("Eat fruit")
}

//蔬菜-食物接口的实现
type vegetable struct {
}

func (t *vegetable) Eat() {
    fmt.Println("Eat vegetable")
}

//NewFood 根据需要的类型,返回实现对象
func NewFood(k FoodKind) Food {
    switch k {
    case MeatKind:
        return &meat{}
    case FruitKind:
        return &fruit{}
    case VegetableKind:
        return &vegetable{}
    }
    return nil
}

上面啰里啰嗦写了一堆代码.该模式的核心函数 \color{red}{func NewFood(k FoodKind) Food} 传入一个参数,返回一个参数.外部import这个simple包的时候,只能通过NewFood获取到Food接口对象.

因为Food接口的实现结构体meat fruit vegetable都是英文首字母小写,外部是无法想这样simple.fruit{}使用到结构体,只能通过NewFood获取.

NewFood内部返回的是指针类型的对象,这个知道为什么吧?

因为返回的参数要求是Food接口的实现类型.我们在实现的时候,只实现了指针的类型.

如果我们想返回值类型应该怎么写呢?

//坚果-食物接口的实现
type nut struct {

}
func (t nut) Eat() {  // !!!注意这里 t nut  而不是t *nut
    fmt.Println("Eat nut")
}

//NewFood 根据需要的类型,返回实现对象
func NewFood(k FoodKind) Food {
    switch k {
     //....省略...
    case NutKind:
        return nut{} // &nut{}  两种写法都可以
    }
    return nil
}

这个就是Golang语言在interface实现上的规则了.记住即可:

指针类型的实现,只实现了指针类型

值类型的实现,即实现了指针类型又实现了值类型.

  • 调用
func main() {
    fmt.Println("简单工厂模式")
    simple.NewFood(simple.MeatKind).Eat()
    simple.NewFood(simple.FruitKind).Eat()
    simple.NewFood(simple.VegetableKind).Eat()
    simple.NewFood(simple.NutKind).Eat()
}
  • 结果
简单工厂模式
Eat meat
Eat fruit
Eat vegetable
Eat nut

2.工厂方法模式

在简单工厂模式的基础上,再进一步的抽象.怎么抽象呢?"简单工厂模式"仅仅只是提供了对外的函数NewFood,如果我把这个函数再封装成interface对外提供能呢?

想想为什么?如果仅仅只有一个函数,所有对象的创建是不是只能在这个函数里面去追加?当对象少的时候,还好说,成百上千的对象,那光看这个一个函数,头都大了.如果能把不同的对象的创建放在类似的函数里面,是不是会清晰很多?

不理解,没关系,看代码,看完保证你理解

package method

import "fmt"

type FoodKind int

const (
    MeatKind FoodKind = iota
    FruitKind
    VegetableKind
    NutKind
)

//Food 食物接口
type Food interface {
    Eat()
}

//肉-食物接口的实现
type meat struct {
}

func (t *meat) Eat() {
    fmt.Println("Eat meat")
}

//水果-食物接口的实现
type fruit struct {
}

func (t *fruit) Eat() {
    fmt.Println("Eat fruit")
}

//蔬菜-食物接口的实现
type vegetable struct {
}

func (t *vegetable) Eat() {
    fmt.Println("Eat vegetable")
}

//坚果-食物接口的实现
type nut struct {
}

func (t nut) Eat() {
    fmt.Println("Eat nut")
}

//-------------干饭人的分割线-----------
//食物工厂
type Factory interface {
    NewFood(k FoodKind) Food //这个函数是不是和简单工厂模式一模一样,而且分割线上面的代码也是一模一样
}

//肉厂
type MeatFactory struct {
}

func (t MeatFactory) NewFood(k FoodKind) Food {
    return &meat{}
}

//水果厂
type FruitFactory struct {
}

func (t FruitFactory) NewFood(k FoodKind) Food {
    return &fruit{}
}

//蔬菜厂
type VegetableFactory struct {
}

func (t VegetableFactory) NewFood(k FoodKind) Food {
    return &vegetable{}
}

//坚果厂
type NutFactory struct {
}

func (t NutFactory) NewFood(k FoodKind) Food {
    return nut{}
}

这里FoodKind其实没有任何实际意义,只是为了让大家看的更直观些.已经是肉厂了,我还传入MeatKind其实没意义,应该传入更细分的肉,例如:鸡肉,鱼肉,猪肉等标识.

  • 调用
func main() {
    fmt.Println("工厂方法模式")
    method.MeatFactory{}.NewFood(method.MeatKind).Eat() //去肉厂吃肉
    method.FruitFactory{}.NewFood(method.FruitKind).Eat() //去水果厂吃水果
    method.VegetableFactory{}.NewFood(method.VegetableKind).Eat()
    method.NutFactory{}.NewFood(method.NutKind).Eat()    
}
  • 结果
工厂方法模式
Eat meat
Eat fruit
Eat vegetable
Eat nut

这样做的好处,我想吃肉,就找肉厂,想吃蔬菜,就去蔬菜场.类似于超市,会分不同的菜品区域一样,简单高效的即可找到所需的东西.

image

"简单工厂模式",更像是去赶集,没有固定的区域,大家卖肉,卖菜全部都混在一起,有可能饶了一大圈才能找到自己想要的东西.

工厂方法模式,其实就是单一职责原则和依赖倒置的体现;

单一职责原则:对于每个具体的实现结构体,我只负责一个具体的功能,不关我的事情,我不做.我只负责写bug,不是我写的我不改.

依赖倒置:具体的实现结构体的方法NewFood的返回参数,依赖于Food接口,而不是具体的某个实现,比如meat,扩展性会更友好.其实也是开放闭合原则的体现.

源码地址 :
https://github.com/gofish2020/gopattern/tree/main/creator

文章首发在微信公众号 "池鱼之交",有需要的可以关注下.


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:池鱼rain

查看原文:1.干饭的才艺(工厂方法模式)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

662 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传