Hello 大家好啊,今天给大家带来的是工厂模式,在 Go 中工厂模式可以说是和 wire 最搭配的助手了,如果你也想掌握 wire 这个酷酷的依赖注入工具,那不妨先从这篇文章开始,为后续的依赖注入打好铺垫
什么是工厂模式
工厂模式(Factory Pattern)是一种创建型设计模式,通过工厂模式我们将对象的创建过程封装起来,使得客户端代码可以请求一个对象,而不需要知道该对象的具体实现细节。
简单来讲,工厂模式是对对象创建过程的封装,从而使我们在使用的过程中无需感知对象的具体实现
应用场景
- 由于在 Go 语言中没有针对类的构造方法进行统一规范,因此在每次创建类实例的时候,都在业务方法中执行实例初始化细节将存在一下问题:
- 业务方法与组件类高度耦合,造成代码冗长、可读性差等问题
- 如果组件类定义发生变更,类的构造流程都需要进行改动,代码改动幅度大、可维护性差
因此我们可以遵循工厂模式的设计思路,实现类和业务方法的解耦、形成公共切面
优势
- 实现类和业务代码之间的解耦,如果类的构造过程发生变更,可以统一在工厂类进行处理,从而对业务方法屏蔽实例化细节
- 如果同时存在多个类聚拢在工厂类进行构造,则各个类的构造流程就形成了公共切面,可以进行公共逻辑的管理与复用
分类
简单工厂模式
首先我们先来看一个例子
package main
import "fmt"
type Animal struct {
Assortment string
}
func (a *Animal) Show() {
switch a.Assortment {
case "dog":
fmt.Println("I am a dog")
case "cat":
fmt.Println("I am a cat")
case "fish":
fmt.Println("I am a fish")
default:
fmt.Println("I am a other animal")
}
}
func NewAnimal(name string) *Animal {
var animal *Animal
switch name {
case "dog":
animal = &Animal{Assortment: "dog"}
case "cat":
animal = &Animal{Assortment: "cat"}
case "fish":
animal = &Animal{Assortment: "fish"}
default:
animal = &Animal{Assortment: "other"}
}
return animal
}
func main() {
dog := NewAnimal("dog")
dog.Show()
cat := NewAnimal("cat")
cat.Show()
fish := NewAnimal("fish")
fish.Show()
}
在上述代码中,针对于 Animal 这个 large 类,存在一下问题:
- 在 Animal 类中包含非常多的条件,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断
- Animal 类的职责过重,它负责初始化和显示所有的动物对象,将各种动物对象的初始化代码和显示代码集中在一个类中实现,违反了*“单一职责原则”*,不利于类的重用和维护
- 当需要增加新类型的动物时,必须修改 Animal 类的构造函数
NewAnimal()
和其他相关方法的代码,违反了*“开闭原则”* - 在业务层中,直接依赖 Animal 类型的构造方法
NewAnimal()
,这样随着 Animal 的越来越复杂,那么业务层的开发逻辑也需要依赖 Animal 模块的更新,且随之改变,这样将导致业务层开发需要观察 Animal 模块做改动,影响业务层的开发效率和稳定性
现在我们来梳理一下其中的依赖关系
graph TD
业务逻辑层 --> 基础类模块
那我们如何进行解耦呢?其实很简单,只需要加一层中间层
graph TD
业务逻辑层 --> 工厂模块 --> 基础类模块
角色与职责分工
工厂(Factory)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
抽象产品(AbstractProduct)角色:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色:简单工厂模式所创建的具体实例对象。
classDiagram
class Factory {
+ CreateProduct() AbstractProduct
}
class ConcreteProduct {
+ AnOperation()
}
class AbstractProduct {
+ AnOperation()
}
Factory ..> ConcreteProduct : 创建
ConcreteProduct --|> AbstractProduct
实现思路
classDiagram
class Factory {
+ CreateProduct() AbstractProduct
}
class Dog {
+ Show()
}
class Cat {
+ Show()
}
class Fish {
+ Show()
}
class Animal {
+ Show
}
Factory ..> Dog : 创建
Factory ..> Cat : 创建
Factory ..> Fish : 创建
Animal <|-- Dog
Animal <|-- Cat
Animal <|-- Fish
- 对于拟构造组件类需要根据其共性,抽离出公共接口
- 在每个具体的组件类对接口加以实现
- 定义一个具体的工厂类,在构造方法中接受具体组件类,完成对该组件类的构造
package animal
import "fmt"
type Animal interface {
Show()
}
type Dog struct {}
func (d *Dog) Show() {
fmt.Printf("I am a dog")
}
type Cat struct {}
func (c *Cat) Show() {
fmt.Printf("I am a cat")
}
type Fish struct {}
func (f *Fish) Show() {
fmt.Printf("I am a fish")
}
package factory
type AnimalFactory struct{}
func NewAnimalFactory() *AnimalFactory {
return &AnimalFactory{}
}
func (f *AnimalFactory) CreateAnimal(type string) (Animal, error) {
switch type {
case "dog":
return &Dog{}, nil
case "cat":
return &Cat{}, nil
case "fish":
return &Fish{}, nil
default:
return nil, fmt.Errorf("Animal type: %s is not supported yet", type)
}
}
package main
func main() {
factory := NewAnimalFactory()
dog, err := factory.CreateAnimal("dog")
if err != err {
panic(err)
}
dog.Show()
cat, err := factory.CreateAnimal("cat")
if err != err {
panic(err)
}
cat.Show()
fish, err := factory.CreateAnimal("fish")
if err != err {
panic(err)
}
fish.Show()
house, err := factory.CreateAnimal("house")
if err != err {
panic(err)
}
house.Show()
}
优势
- 简单直观
- 实现了对象创建于使用的分离
- 构造各类组件时聚拢效果好,公共切面全面
劣势
- 工厂类职责过于集中
- 系统中类的个数增加,增加系统复杂度与理解难度
- 组件类扩展时,需要直接修改工厂的组件构造方法,违反开闭原则
适用场景
-
工厂类负责创建的对象较少
-
对与对象创建过程低依赖
工厂方法模式
在简单工厂模式中,我们违反了开闭原则,在这里我们对其设计流程进行一定修改,得到工厂方法模式
角色与职责分工
抽象工厂(Abstract Factory)角色:工厂方法模式的核心,任何工厂类都必须实现这个接口。
工厂(Concrete Factory)角色:具体工厂类是抽象工厂的一个实现,负责实例化产品对象。
抽象产品(Abstract Product)角色:工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色:工厂方法模式所创建的具体实例对象。
classDiagram
class AbstractFactory {
+ CreateProduct(): AbstractProduct
}
class ConcreteProduct {
+ AnOperation()
}
class AbstractProduct {
+ AnOperation()
}
class Factory {
+ CreateProduct(): AbstractProduct
}
Factory ..> ConcreteProduct : 创建
Factory ..|> AbstractFactory
ConcreteProduct ..|> AbstractProduct
实现思路
classDiagram
class Animal {
+ Show()
}
class Dog {
+ Show()
}
class Cat {
+ Show()
}
class Fish {
+ Show()
}
class DogFactory {
+ CreateAnimal():Animal
}
class CatFactory {
+ CreateAnimal():Animal
}
class FishFactory {
+ CreateAnimal():Animal
}
class AnimalFactory {
+ CreateAnimal():Animal
}
DogFactory ..> Dog : 创建
CatFactory ..> Cat : 创建
FishFactory ..> Fish : 创建
DogFactory ..|> AnimalFactory
CatFactory ..|> AnimalFactory
FishFactory ..|> AnimalFactory
Dog ..|> Animal
Cat ..|> Animal
Fish ..|> Animal
- 关于组件的定义模式不变。由一个抽象的 Animal interface,多个具体的实现 Dog、Cat、Fish
- 将工厂类 AnimalFactory 由具体的实现类改为抽象的 interface
- 针对每类,提供出一个具体的工厂实现类,如 DogFactory、CatFactory、FishFactory
package abstract
type Animal interface {
Show()
}
type AnimalFactory interface {
CreateAnimal() Animal
}
package animal
import "fmt"
type Dog struct {}
func (d *Dog) Show() {
fmt.Printf("I am a dog")
}
type Cat struct {}
func (c *Cat) Show() {
fmt.Printf("I am a cat")
}
type Fish struct {}
func (f *Fish) Show() {
fmt.Printf("I am a fish")
}
package factory
type DogFactory struct {
AnimalFactory
}
func NewDogFactory AnimalFactory {
return DogFactory{}
}
func (f *DogFactory) CreateAnimal() Animal {
return &Dog{}
}
type CatFactory struct {
AnimalFactory
}
func NewCatFactory AnimalFactory {
return DogFactory{}
}
func (f *CatFactory) CreateAnimal() Animal {
return &Cat{}
}
type FishFactory struct {
AnimalFactory
}
func NewFishFactory AnimalFactory {
return DogFactory{}
}
func (f *FishFactory) CreateAnimal() Animal {
return &Fish{}
}
package main
func main() {
dogFactory := NewDogFactory()
dog := dogFactory.CreateAnimal()
dog.Show()
catFactory := NewCatFactory()
cat := catFactory.CreateAnimal()
cat.Show()
fishFactory := NewFishFactory()
fish := fishFactory.CreateAnimal()
fish.Show()
}
优点
- 实现了对象创建与使用的分离
- 满足**开闭原则,**组件类扩展时无需更改接口与原组件类,代码入侵小,不会影响系统稳定性
缺点
- 增加系统中类的个数,系统复杂度与理解难度增加
- 代码冗余度高
- 公共切面少,公共逻辑反复声明,代码复用率低