这是我参与「第五届青训营 」伴学笔记创作活动的第 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")
}
在上述代码中,存在以下几个问题:
- 在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()
}
此时我们需要新增加一个桃子类,只需要新增加一个桃子类并实现其对应的方法便可,不需要修改代码,符合开闭原则。
优点:
- 不需要记住具体类名,甚至连具体参数都不用记忆
- 实现了对象创建和使用的分离
- 系统的可扩展性也就变得非常好,无需修改接口和原类
- 对于新产品的创建,符合开闭原则
缺点:
- 增加系统中类的个数,复杂度和理解度增加
- 增加了系统的抽象性和理解难度
适用场景:
- 客户端不知道它所需要的对象的类。
- 抽象工厂类通过其子类来指定创建哪个对象