在设计模式中,践行的理念就是面向抽象编程,而面向抽象编程的一个好处在于可以控制变化,这种变化的概念很丰富,例如具体的 UI 变化,或者是抽象的实现变化。在长期的软件开发实践中,前人已经根据自身的经验,总结出了一套适应变化的常见套路,而这篇文章要介绍的就是其中一个比较常用的简单工厂模式。本文将通过 Go 语言(Golang) 进行一个简单的实践,展示一下简单工厂模式有什么好处。
一个原始的例子
在开始介绍设计模式之前,我先介绍一个场景,这个场景是这样的,我有好几个云环境,例如 GCP / DO / Vultr,然后我开发了一套管理后台用于管理我在这些云环境的资源(主要是 VM),那么对于配置,我是简单抽象成了这样:
然后假设我要操作一个 VM 的话,那么我可能会这么写:
这里我抽象了一个 interface,用于表示云管理的操作集合,然后我又各自实现对应的具体操作:
整个目录结构是这样的:
[root@liqiang.io]# tree
.
├── cloud_manager.go
├── config.go
└── service
├── gcp.go
├── do.go
└── vultr.go
那么删除的操作,我在 main.go
里面会这么写:
这样代码是没有问题,可以走通,但是问题在于如果我再操作 Do 或者 Vultr 的话,就需要再构造两个 Manager 了,不利于后续扩展。
简单工厂模式
为了让扩展更简单,这里有个常用操作就是可以创建一个方法用于构造具体的 Manager:
这样,我的 main 函数就可以变得更加简洁灵活了:
这样你就不用在使用 Manager 的地方去关心你具体操作的是哪个云了,只需要知道当前使用的是什么配置,那么对应的 Manager 就是这个配置相应的云了。当然,这样的代码虽然解决了问题,但是不利于测试,所以,为了方便测试,我会将这个工厂方法抽象成一个 Interface,这样就可以很方便得进行 UT 了:
评价
优点
- 消费者无需关心创建产品的细节,实现了责任的分割;
- 让代码解耦,消费者无需知道真正的实现在哪,是谁实现的
缺点
- 每次增加一个新的产品都需要修改一下工厂方法(CloudType 并不是必须修改的,可以直接换成 String)
- 云平台少的时候代码还挺整洁,但是,如果我要对接的云平台多了之后工厂方法会很崩溃
- 管理功能受限于抽象,例如我的 Manager 只有 VM 管理功能,要想添加存储管理功能就需要做很多修改