什么什么有人问我 wire 怎么用?不妨先来学学工厂模式吧!🏭🏭🏭

214 阅读6分钟

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 类,存在一下问题:

  1. 在 Animal 类中包含非常多的条件,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断
  2. Animal 类的职责过重,它负责初始化和显示所有的动物对象,将各种动物对象的初始化代码和显示代码集中在一个类中实现,违反了*“单一职责原则”*,不利于类的重用和维护
  3. 当需要增加新类型的动物时,必须修改 Animal 类的构造函数 NewAnimal() 和其他相关方法的代码,违反了*“开闭原则”*
  4. 在业务层中,直接依赖 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()
}

优势

  • 简单直观
  • 实现了对象创建于使用的分离
  • 构造各类组件时聚拢效果好,公共切面全面

劣势

  • 工厂类职责过于集中
  • 系统中类的个数增加,增加系统复杂度与理解难度
  • 组件类扩展时,需要直接修改工厂的组件构造方法,违反开闭原则

适用场景

  1. 工厂类负责创建的对象较少

  2. 对与对象创建过程低依赖

工厂方法模式

在简单工厂模式中,我们违反了开闭原则,在这里我们对其设计流程进行一定修改,得到工厂方法模式

角色与职责分工

抽象工厂(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()
}

优点

  • 实现了对象创建与使用的分离
  • 满足**开闭原则,**组件类扩展时无需更改接口与原组件类,代码入侵小,不会影响系统稳定性

缺点

  • 增加系统中类的个数,系统复杂度与理解难度增加
  • 代码冗余度高
  • 公共切面少,公共逻辑反复声明,代码复用率低