工厂模式

工厂模式分类:

简单工厂模式、工厂方法模式、抽象工厂模式、容器工厂模式

使用背景:

在Go语言中没有针对类的构造器方法定义统一的规范,倘若每次需要创建类的实例时,都需要在业务方法中事无俱细地执行实例初始化的细节,那么会存在缺陷的包括:

  • 业务方法和组件类之间产生过高的耦合度,需要了解组件类的过多细节
  • 如果组件类的定义发生更改,那么散落在各处的业务方法中对类的构造流程都需要配合改动

如何解决上述问题?只需要只能一个中间层解决就可以了!

在这里插入图片描述

工厂类可以带来的好处:

  • 实现类和业务方法之间的解耦,如果类的构造过程发生变更,可以统一收口在工厂类中进行处理,从而对业务方法屏蔽相关细节
  • 倘若有多个类都聚拢在工厂类中进行构造,这样各个类的构造流程中就天然形成了一个公共的切面,可以进行一些公共逻辑的执行

简单工厂模式:

简单定义:

可以根据不同参数返回不同的类的实例,被创建的类通常有共同的父类

假设我们有这样场景:

  • 有个抽象的Fruit interface !赋予Fruit一个Eat的方法
  • 有三个Fruit的具体实现类,Orange、Strawberry和cherry,都是实现了Eat方法
  • 有一个具体Factory 可以使用方法CreateFruit 根据用户的指定类型创建不同Fruit的实例!

UML类图如下:

在这里插入图片描述

具体实现如下:

// Fruit接口
type Fruit interface{
    Eat()
}
type Orange struct{
    name string
}
func (o *Orange)Eat(){
    fmt.Println("Orange implement Fruit Eat() method...")
}
func NewOrange(name string) Fruit{
    return &Orange{
        name:name,
    }
}
type Strawberry struct{
    name string
}
func (s *Strawberry)Eat(){
    fmt.Println("Strawberry implement Fruit Eat() method...")
}
func NewStrawberry(name string) Fruit{
    return &Strawberry{
        name:name,
    }
}
type Cherry struct{
    name string
}
func (c *Cherry)Eat(){
    fmt.Println("Cherry implement Fruit Eat() method...")
}
func NewCherry(name string) Fruit{
    return &Cherry{
        name:name,
    }
}

下面是关于生产水果的工厂类 FruitFactory 的定义,其中 CreateFruit 方法是用于生产水果的核心方法:

  • 利用工厂生产三类时存在的公共切面,进行随机数的取值,用来给生产出来的水果命名
  • 根据使用方传入的水果类型 typeNmae ,调用对应水果类型的构造器方法,并将生产出来的水果进行返回
  • 如果使用方法传入的水果类型 typeNmae 非法,则对外抛出错误
// FruitFactory 定义工厂类
type FruitFactory struct{
    
}
// NewFruitFactory 获取工厂实例
func NewFruitFactory() *FruitFactory{
    return &FruitFactory{}
}
// CreateFruit 根据类型返回不同的实例对象
func (f *FruitFactory)CreateFruit(typeName string) (Fruit,error){
    src:=rand.NewSource(time.Now().UnixNano())
    //New返回一个新的Rand,它使用来自src的随机值来生成其他随机值。
    rander:=rand.New(src)
    name:=strconv.Itoa(rander.Int())
    
    switch typeName{
        case "orange":
        return NewOrange(name),nil
        case "strawberry":
        return NewStrawberry(name),nil
        case "cherry":
        return NewCherry(name),nil
        default:
        return nil,fmt.Errorf("fruit typeName: %s is not supported yet", typeName)
    }
}


测试:

func Test_factory(t *testing.T) {
    // 构造工厂
    fruitFactory := NewFruitFactory()


    // 尝个橘子
    orange, _ := fruitFactory.CreateFruit("orange")
    orange.Eat()


    // 来颗樱桃
    cherry, _ := fruitFactory.CreateFruit("cherry")
    cherry.Eat()


    // 来个西瓜,因为未实现会报错
    watermelon, err := fruitFactory.CreateFruit("watermelon")
    if err != nil {
        t.Error(err)
        return
    }
    watermelon.Eat()
}

工厂模式的优缺点:

  • 优点:实现对象的创建和分离,对于客户端的程序员来说不需要关心怎么创建的,只关心怎么使用就可以了
  • 缺点:不够灵活,不利于类的拓展

工厂方法模式

为了解决简单工厂模式中存在的问题,对设计流程进行修改:

  • 关于组件的定义模式不变. 一个抽象的 Fruit interface,多个具体的水果实现 Orange、Strawberry、Cherry
  • 将工厂类 FruitFactory 由具体的实现类改为抽象的 interface
  • 针对每类水果,提供出一个具体的工厂实现类,如 OrangeFactory、StrawberryFactory、CherryFactory

UML类图如下:

在这里插入图片描述

在工厂方法模式中,Fruit interface 以及几个具体实现类 Orange、Strawberry 和 Cherry 的定义和简单工厂模式如出一辙!

与简单工厂模式的区别是,水果工厂类 FruitFactory 在此处变成一个抽象的 interface,且针对每种具体的水果实现类需要对应地声明一种工厂实现类,包括 OrangeFactory、StrawberryFactory、CherryFactory,具体的实现代码如下:

type FruitFactory interface {
	CreateFruit() Fruit
}
type OrangeFactory struct {
}

func (o *OrangeFactory) CreateFruit() Fruit {
	return NewOrange("")
}

type StrawberryFactory struct {
}

func (s *StrawberryFactory) CreateFruit() Fruit {
	return NewStrawberry("")
}

type CherryFactory struct {
}

func (c *CherryFactory) CreateFruit() Fruit {
	return NewCherry("")
}
func NewOrangeFactory() FruitFactory {
	return &OrangeFactory{}
}
func NewStrawberryFactory() FruitFactory {
	return &StrawberryFactory{}
}
func NewCherryFactory() FruitFactory {
	return &CherryFactory{}
}

这样的设计模式下,即便后续有频繁扩展水果实现类的需求,也无须对老模块的代码进行修改,而是需要扩展实现一个水果 Fruit 的实现类以及对应的水果工厂实现类即可,比如,倘若此处我们需要在水果列表中扩展一个“西瓜”的话,那么需要新增的代码如下:

type Watermelon struct {
    name string
}


func NewWatermelon(name string) Fruit {
    return &Watermelon{
        name: name,
    }
}


func (w *Watermelon) Eat() {
    fmt.Printf("i am watermelon: %s, i am about to be eaten...", w.na


type WatermelonFactory struct {
}


func NewWatermelon() FruitFactory {
    return &WatermelonFactory{}
}


func (w *WatermelonFactory) CreateFruit() Fruit {
    return NewWatermelon("")
}

工厂方法模式相较于简单工厂模式而言,解决了扩展水果类不满足开闭原则的问题,然而工厂方法模式也有其固有的缺陷:

  • 需要为每个水果单独实现一个工厂类,代码冗余度较高
  • 原本构造多个水果类时存在的公共切面不复存在,一些通用的逻辑需要在每个水果工厂实现类中重复声明一遍
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐