go语言设计模式-工厂模式

发布时间:2023年12月29日

建造型设计模式—工厂模式

简单工厂

根据条件对外提供创建不同的类。仿佛根据顾客要求,工厂生产不同的产品。

Go 语言没有构造函数,一般会定义 NewXXX 函数来初始化相关类。NewXXX 函数返回接口时就是简单工厂模式。

一个简单的应用场景,这个应用场景里会提供很多语言的打印机,他们都源于一个 Printer 接口。

// Printer 简单工厂要返回的接口类型
type Printer interface {
 Print(name string) string
}

程序通过简单工厂向客户端提供需要的语种的打印机。

func NewPrinter(lang string) Printer {
 switch lang {
 case "cn":
  return new(CnPrinter)
 case "en":
  return new(EnPrinter)
 default:
  return new(CnPrinter)
 }
}

目前这个场景里我们先提供两个语种的打印机,他们都是 Printer 接口的具体实现类型。

// CnPrinter 是 Printer 接口的实现,它说中文
type CnPrinter struct {}

func (*CnPrinter) Print(name string) string {
 return fmt.Sprintf("你好, %s", name)
}

// EnPrinter 是 Printer 接口的实现,它说中文
type EnPrinter struct {}

func (*EnPrinter) Print(name string) string {
 return fmt.Sprintf("Hello, %s", name)
}

客户端只需要告诉工厂想要哪个语种的打印机产品,工厂就会把产品给返回给客户端。

printer := NewPrinter("en")
fmt.Println(printer.Print("Bob"))

简单工厂模式主要包含3个角色。

  • 简单工厂:是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品:是简单工厂创建的所有对象的抽象父类/接口,负责描述所有实例的行为。
  • 具体产品:是简单工厂模式的创建目标。

简单工厂的优点是,简单,缺点是如果具体产品扩产,就必须修改工厂内部,增加Case,一旦产品过多就会导致简单工厂过于臃肿,为了解决这个问题,才有了下一级别的工厂模式–工厂方法。

工厂方法模式

工厂方法模式(Factory Method Pattern)又叫作多态性工厂模式,指定义一个创建对象的接口,但由实现这个接口的工厂类来决定实例化哪个产品类,工厂方法把类的实例化推迟到子类中进行。

在工厂方法模式中,不再由单一的工厂类生产产品,而是由工厂类的子类实现具体产品的创建

即把同一类型的不同工厂类的共同属性、方法进一步抽象出一个母工厂,和母类。

因此,当增加一个产品时,只需增加一个相应的工厂类的子类, 以解决简单工厂生产太多产品时导致其内部代码臃肿(switch … case分支过多)的问题。

下面举个简单的例子来理解工厂方法的设计思想,考虑有这样一个生产计算器的工厂,每类计算器产品都由一个子工厂负责生产。

Go中没有继承,所以这里说的工厂子类,其实是直接实现工厂接口的具体工厂类

// OperatorFactory 工厂接口,由具体工厂类来实现
type OperatorFactory interface {
 Create() MathOperator
}

// MathOperator 实际产品实现的接口--表示数学运算器应该有哪些行为
type MathOperator interface {
 SetOperandA(int)                                             
 SetOperandB(int)
 ComputeResult() int
}

假定程序可以生产两类计算器,加法计算器和乘法计算器,也就是在工厂方法模式中,存在两个子类工厂。

//PlusOperatorFactory 是 PlusOperator 加法运算器的工厂类
type PlusOperatorFactory struct{}

func (pf *PlusOperatorFactory) Create() MathOperator {
 return &PlusOperator{
  BaseOperator: &BaseOperator{},
 }
}

// MultiOperatorFactory 是乘法运算器产品的工厂
type MultiOperatorFactory struct {}

func (mf *MultiOperatorFactory) Create() MathOperator{
 return &MultiOperator{
  BaseOperator: &BaseOperator{},
 }
}

这两个子类工厂分别用来生产加法和乘法计算器

// BaseOperator 是所有 Operator 的基类
// 封装公用方法,因为Go不支持继承,具体Operator类
// 只能组合它来实现类似继承的行为表现。
type BaseOperator struct {
 operandA, operandB int
}

func (o *BaseOperator) SetOperandA(operand int) {
 o.operandA = operand
}

func (o *BaseOperator) SetOperandB(operand int) {
 o.operandB = operand
}

//PlusOperatorFactory 是 PlusOperator 加法运算器的工厂类
type PlusOperatorFactory struct{}

func (pf *PlusOperatorFactory) Create() MathOperator {
 return &PlusOperator{
  BaseOperator: &BaseOperator{},
 }
}

//PlusOperator 实际的产品类--加法运算器
type PlusOperator struct {
 *BaseOperator
}

//ComputeResult 计算并获取结果
func (p *PlusOperator) ComputeResult() int {
 return p.operandA + p.operandB
}

// MultiOperatorFactory 是乘法运算器产品的工厂
type MultiOperatorFactory struct {}

func (mf *MultiOperatorFactory) Create() MathOperator{
 return &MultiOperator{
  BaseOperator: &BaseOperator{},
 }
}

// MultiOperator 实际的产品类--乘法运算器
type MultiOperator struct {
 *BaseOperator
}
func (m *MultiOperator) ComputeResult() int {
 return m.operandA * m.operandB
}

测试运行–客户端使用子类工厂创建产品实例。

func main() {
 var factory OperatorFactory
 var mathOp MathOperator
 factory = &PlusOperatorFactory{}
 mathOp = factory.Create()
 mathOp.SetOperandB(3)
 mathOp.SetOperandA(2)
 fmt.Printf("Plus operation reuslt: %d\n", mathOp.ComputeResult())

 factory= &MultiOperatorFactory{}
 mathOp = factory.Create()
 mathOp.SetOperandB(3)
 mathOp.SetOperandA(2)
 fmt.Printf("Multiple operation reuslt: %d\n", mathOp.ComputeResult())
}

工厂方法模式的优点

  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

工厂方法模式的缺点

  • 类的个数容易过多,增加复杂度。
  • 增加了系统的抽象性和理解难度。
  • 只能生产一种产品,此弊端可使用抽象工厂模式解决。

抽象工厂

即对于更大的项目,具有更多的不同类型的类,此时把所有工厂类的共同属性进一步抽象出一个包含不同类型工厂的公共母工厂。

example:抽象工厂有两个实际工厂类一个是华为的工厂,一个是小米的工厂,他们用来实际生产自家的产品设备。

抽象工厂也具备工厂方法把产品的创建推迟到工厂子类去做的特性,假如未来加入了 VIVO 的产品,我们就可以通过再创建 VIVO 工厂子类来扩展。

type AbstractFactory interface {
 CreateTelevision() ITelevision
 CreateAirConditioner() IAirConditioner
}

type ITelevision interface {
 Watch()
}

type IAirConditioner interface {
 SetTemperature(int)
}

type HuaWeiFactory struct{}

func (hf *HuaWeiFactory) CreateTelevision() ITelevision {
 return &HuaWeiTV{}
}
func (hf *HuaWeiFactory) CreateAirConditioner() IAirConditioner {
 return &HuaWeiAirConditioner{}
}

type HuaWeiTV struct{}

func (ht *HuaWeiTV) Watch() {
 fmt.Println("Watch HuaWei TV")
}

type HuaWeiAirConditioner struct{}

func (ha *HuaWeiAirConditioner) SetTemperature(temp int) {
 fmt.Printf("HuaWei AirConditioner set temperature to %d ℃\n", temp)
}

type MiFactory struct{}

func (mf *MiFactory) CreateTelevision() ITelevision {
 return &MiTV{}
}
func (mf *MiFactory) CreateAirConditioner() IAirConditioner {
 return &MiAirConditioner{}
}

type MiTV struct{}

func (mt *MiTV) Watch() {
 fmt.Println("Watch HuaWei TV")
}

type MiAirConditioner struct{}

func (ma *MiAirConditioner) SetTemperature(temp int) {
 fmt.Printf("Mi AirConditioner set temperature to %d ℃\n", temp)
}


func main() {
 var factory AbstractFactory
 var tv ITelevision
 var air IAirConditioner

 factory = &HuaWeiFactory{}
 tv = factory.CreateTelevision()
 air = factory.CreateAirConditioner()
 tv.Watch()
 air.SetTemperature(25)

 factory = &MiFactory{}
 tv = factory.CreateTelevision()
 air = factory.CreateAirConditioner()
 tv.Watch()
 air.SetTemperature(26)
}
  • 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
  • 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
  • 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。
  • 当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

抽象工厂模式的优点

  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品族。
  • 抽象工厂增强了程序的可扩展性,对于新产品族的增加,只需实现一个新的具体工厂即可,不需要对已有代码进行修改,符合开闭原则。

抽象工厂模式的缺点

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
  • 增加了系统的抽象性和理解难度。

参考:公众号陪计算机度过漫长岁月

文章来源:https://blog.csdn.net/zhaicheng55/article/details/135287251
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。