脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Golang设计模式工厂模式实战写法示例详解

Golang设计模式工厂模式实战写法示例详解

2022-08-29 12:12ag9920 Golang

这篇文章主要为大家介绍了Golang 工厂模式实战写法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

拆出主板

今天带大家看一下怎么用 Go 写工厂模式的代码,我们来学习一个实战案例。这个写法笔者日常经常使用,能够很有效地帮助大家实现 Separation of Concerns。

主板就是一个程序的主流程。比如我们要基于一份学习资料来消化,吸收知识。我们可能有下面几步流程:

  • 准备好笔记本;
  • 打开资料;
  • 阅读资料内容,思考并记录关键点到笔记本上;
  • 做资料里包含的练习题;
  • 归纳总结,验证掌握程度。

这个资料,可以是纸质书籍,可以是电子书,可以是某个平台的专栏,形式有很多,但我们不 care,因为在主题流程中,只需要它是个资料,有资料的能力即可。

换句话说,我们把资料转换成一个 interface,定义如下:

?
1
2
3
4
type KnowledgeMaterial interface{
    GetContent() string
    GetExercises() []string
}

能给我们主体内容,能给我们练习题,满足这两点就够了。

所以,主板本质上是不 care 具体这个资料是什么的。

扩展则是基于 interface 的实现,或者类比一下 adapter,就是个适配器。我们可以定义出来 Book, Ebook, Column 各种各样的扩展,它们都实现了这个 KnowledgeMaterial 接口。

很多同学写代码的时候,拆不开主板,不知道自己的核心流程是什么,这一点是非常重要的。拆不出来【主流程】,就意味着你需要针对某个实体实现逻辑时,直接依赖了这个【实现】。

比如我们上面的 case,没有 KnowledgeMaterial 接口,你的流程变成了,翻开纸质书第一页,看看目录,然后翻到第一章,开始阅读书上的文字。。。。

这是很可怕的一件事,意味着一旦结构变了,你的代码是不可能适配的。你会需要各种 if else 来判断到底是哪种类型。如果后来又来了一种学习资料,叫做【视频课程】,这时候怎么办呢?

没有页供你翻了,你面对的实体变成了视频内容,想要适配,就势必不是容易的事。

所以,大家一定要练习这个能力,遇到问题,思考自己的主流程是什么,拆出主板,然后明确你对业务实体的诉求是什么,能否抽象化。

是一个实现了KnowledgeMaterial 接口的任意实体就 ok?还是必须得是 Book 这个具体的结构体才 ok?

如果你需要的只是个接口,能够抽象简化,就尽量用我们今天要说的工厂模式来做,这样你的主流程心智负担会小很多,此后新增扩展成本也很小。

工厂模式流程

  • 抽象出对实体的能力要求,变成接口;
  • 实现工厂,支持适配器注册,支持根据类型获取对应的接口实现;
  • 主流程只依赖接口完成;
  • 将你的扩展,变成 adapter 适配器,实现接口所要求的的能力;
  • 将你的适配器通过第二步里提到的方法,注册到工厂里。

这样的好处就在于,主板和扩展隔离,新增扩展的时候,只需要新增,不需要动主流程,不需要动其他扩展,避免了一大堆 if else 的写法。

代码实战

我们结合一开始提到的 KnowledgeMaterial 接口来简单示例一下。

抽象能力,定义接口

?
1
2
3
4
type KnowledgeMaterial interface{
    GetContent() string
    GetExercises() []string
}

实现工厂,支持注册和获取实现

新建一个 factory.go 文件,填充如下内容:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type KnowledgeAdapterFactory struct {
    sync.RWMutex
    adapters []KnowledgeAdapter
}
var (
    knowledgeAdapterFactory = KnowledgeAdapterFactory{
        adapters: []KnowledgeAdapter{},
    }
)
// RegisterKnowledgeAdapter 注册新的知识资料适配器
func RegisterKnowledgeAdapter(adapter KnowledgeAdapter) {
    knowledgeAdapterFactory.Lock()
    knowledgeAdapterFactory.adapters = append(knowledgeAdapterFactory.adapters, adapter)
        knowledgeAdapterFactory.Unlock()
}
// GetAllKnowledgeAdapters 获取所有知识资料适配器
func GetAllKnowledgeAdapters() []KnowledgeAdapter {
    return knowledgeAdapterFactory.adapters
}

主流程只依赖接口完成

重点关注和 adapter 相关的逻辑,其他部分省略:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func LearnKnowledge() {
    //准备好笔记本
    notes := openNotesForWrite()
    for _, adapter := range GetAllKnowledgeAdapters() {
        content := adapter.GetContent()
        // 阅读资料内容,思考并记录关键点到笔记本上
        writeNotes(content)
        // 做资料里包含的练习题
        for _, ex := range adapter.GetExercises() {
            doExecise(ex)
        }
    }
    // 归纳总结,验证掌握程度
    summary()
}

扩展 => 适配器,实现接口

新建一个包:book,用于实现纸质书籍的适配器。在其中新建 adapter.go 文件,填充如下代码

?
1
2
3
4
5
6
7
type Adapter struct {}
func (a *Adapter) GetContent() string {
    return "xxx"
}
func (a *Adapter) GetExercises() []string {
    return []string{"xxx"}
}

注册适配器到工厂里

这里写法其实相对灵活,很多人会选择直接在工厂定义的 factory.go 写注册逻辑,我个人不太喜欢这样。这就意味着每次新增适配器,都需要动工厂。

比较推荐直接在适配器的 init() 函数中完成注册,然后在 main 函数启动时 import 包进来,就执行了 init 函数。

这样写的好处在于当你新增一个扩展的时候,主流程和工厂都不需要动,只新增文件就好。

我们可以把上面的 adapter.go 新增一个函数即可:

?
1
2
3
4
5
6
7
8
9
10
type Adapter struct {}
func init() {
    RegisterKnowledgeAdapter(&Adapter{})
}
func (a *Adapter) GetContent() string {
    return "xxx"
}
func (a *Adapter) GetExercises() []string {
    return []string{"xxx"}
}

小结

工厂模式是一个很简单,容易上手的写法,重点还是在于大家要区分开主板和扩展,通过注册方式填充适配器,而不是通过 if else 来区分。希望今天介绍的写法对你有帮助,这里还可以有很多变形,本质是类似的。

以上就是Golang 工厂模式实战写法示例详解的详细内容,更多关于Golang 工厂模式的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7136852697872859173

延伸 · 阅读

精彩推荐
  • Golang让goland支持proto文件类型的实现

    让goland支持proto文件类型的实现

    这篇文章主要介绍了让goland支持proto文件类型的实现操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    peachesTao15692021-02-24
  • GolangGo语言使用goroutine及通道实现并发详解

    Go语言使用goroutine及通道实现并发详解

    这篇文章主要为大家介绍了Go语言使用goroutine及通道实现并发详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    陈博士11912022-08-24
  • GolangGo语言计算两个经度和纬度之间距离的方法

    Go语言计算两个经度和纬度之间距离的方法

    这篇文章主要介绍了Go语言计算两个经度和纬度之间距离的方法,涉及Go语言相关数学函数的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    不吃皮蛋3112020-04-14
  • Golanggolang日志包logger的用法详解

    golang日志包logger的用法详解

    这篇文章主要介绍了golang日志包logger的用法详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    老衲一头头秀发5262021-06-18
  • GolangGoland使用Go Modules创建/管理项目的操作

    Goland使用Go Modules创建/管理项目的操作

    这篇文章主要介绍了Goland使用Go Modules创建/管理项目的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    deantzzz8032021-06-21
  • GolangGolang Http 验证码示例实现

    Golang Http 验证码示例实现

    这篇文章主要介绍了Golang Http 验证码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小...

    xialeistudio4742020-09-06
  • Golang浅谈beego默认处理静态文件性能低下的问题

    浅谈beego默认处理静态文件性能低下的问题

    下面小编就为大家带来一篇浅谈beego默认处理静态文件性能低下的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看...

    jingxian5702020-05-07
  • Golang解析Golang中引用类型是否进行引用传递

    解析Golang中引用类型是否进行引用传递

    这篇文章主要为大家介绍了Golang中引用类型是否进行引用传递剖析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加...

    刘悦的技术博客8132022-08-29