这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
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")
}
在上述代码中,存在以下几个问题:
- 在NewFruit函数中有很多的 switch... case... 语句,整个函数的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。
- Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水果对象的初始化代码和显示代码集中在一个类中实现,违反了”单一职责原则“,不利于类的重用和维护。
- 当需要新增新类型的水果时,必须修改Fruit类的构造函数NewFruit()和其他相关方法源代码,违反了”开闭原则“。
2. 简单工厂模式
用户只需要接触到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,违背了开闭原则。
于是便有了工厂方法模式。
代码如下:
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()
}
此时我们需要新增加一个桃子类,只需要新增加一个桃子类并实现其对应的方法便可,不需要修改代码,符合开闭原则。
优点:
- 不需要记住具体类名,甚至连具体参数都不用记忆
- 实现了对象创建和使用的分离
- 系统的可扩展性也就变得非常好,无需修改接口和原类
- 对于新产品的创建,符合开闭原则
缺点:
- 增加系统中类的个数,复杂度和理解度增加
- 增加了系统的抽象性和理解难度
适用场景:
- 客户端不知道它所需要的对象的类。
- 抽象工厂类通过其子类来指定创建哪个对象
4. 抽象工厂模式
示例代码如下:
package main
import "fmt"
// ========== 抽象层 ===============
type AbstractApple interface {
ShowApple()
}
type AbstractBanana interface {
ShowBanana()
}
type AbstractPear interface {
ShowPear()
}
// 抽象的工厂
type AbstractFactory interface {
CreateApple() AbstractApple
CreateBanana() AbstractBanana
CreatePear() AbstractPear
}
// ============= 实现层 ==============
// 中国族产品
type ChinaApple struct{}
func (ca *ChinaApple) ShowApple() {
fmt.Println("I am Chinese Apple")
}
type ChinaBanana struct{}
func (cb *ChinaBanana) ShowBanana() {
fmt.Println("I am China Banana")
}
type ChinaPear struct{}
func (cp *ChinaPear) ShowPear() {
fmt.Println("I am China Pear")
}
type ChinaFactory struct{}
func (cf *ChinaFactory) CreateApple() AbstractApple {
var apple AbstractApple
apple = new(ChinaApple)
return apple
}
func (cf *ChinaFactory) CreateBanana() AbstractBanana {
var banana AbstractBanana
banana = new(ChinaBanana)
return banana
}
func (cf *ChinaFactory) CreatePear() AbstractPear {
var pear AbstractPear
pear = new(ChinaPear)
return pear
}
// 日本族产品
type JapanApple struct{}
func (ca *JapanApple) ShowApple() {
fmt.Println("I am Chinese Apple")
}
type JapanBanana struct{}
func (cb *JapanBanana) ShowBanana() {
fmt.Println("I am Japan Banana")
}
type JapanPear struct{}
func (cp *JapanPear) ShowPear() {
fmt.Println("I am Japan Pear")
}
type JapanFactory struct{}
func (cf *JapanFactory) CreateApple() AbstractApple {
var apple AbstractApple
apple = new(JapanApple)
return apple
}
func (cf *JapanFactory) CreateBanana() AbstractBanana {
var banana AbstractBanana
banana = new(JapanBanana)
return banana
}
func (cf *JapanFactory) CreatePear() AbstractPear {
var pear AbstractPear
pear = new(JapanPear)
return pear
}
// 逻辑层
func main() {
// 需求1:需要中国的苹果、香蕉、梨
var cFac AbstractFactory
cFac = new(ChinaFactory)
// 生成苹果
var cApple AbstractApple
cApple = cFac.CreateApple()
cApple.ShowApple()
}
优点:
- 拥有工厂模式的优点
- 当一个产品族的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 增加新的产品族很方便,无需修改已有系统,符合“开闭原则”。
缺点:
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
- 产品等级稳定之后。设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。