go设计模式之抽象工厂模式

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个"产品族",由同一个工厂来统一生产

抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。

  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。

抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。

工厂模式的退化

当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。

参与者

  • AbstractFactory

    声明一个创建抽象产品对象的操作接口。

  • ConcreteFactory

    实现创建具体产品对象的操作。

  • AbstractProduct

    为一类产品对象声明一个接口。

  • ConcreteProduct

    定义一个将被相应的具体工厂创建的产品对象。

    实现AbstractProduct接口。

  • Client

    仅使用由AbstractFactory和AbstractProduct类声明的接口。

案例1

场景:

如果你想要购买一组运动装备,需要购买鞋子和帽子,有adidas和nike品牌。相信你会想去购买同一品牌的商品, 这样商品之间能够互相搭配起来。

  • 抽象工厂接口:ISportsFactory
  • 具体工厂:AdidasFactory、NikeFactory
  • 抽象产品:IShoe、IHat
  • 具体产品:AdidasShoe、AdidaHat、NikeShoe、NikeHat

抽象产品

iHat.go

go 复制代码
package main

type IHat interface {
	setLogo(logo string)
	setColor(color string)
	getLogo() string
	getColor() string
}

iShoe.go

go 复制代码
package main

type IShoe interface {
	setLogo(logo string)
	setSize(size int)
	getLogo() string
	getSize() int
}

具体产品

adidasHat.go

go 复制代码
package main

type AdidasHat struct {
	logo string
	color string
}

func (a *AdidasHat) setLogo(logo string) {
	a.logo = logo
}

func (a *AdidasHat) getLogo() string {
	return a.logo
}

func (a *AdidasHat) setColor(color string) {
	a.color = color
}

func (a *AdidasHat) getColor() string {
	return a.color
}

adidasShoe.go

go 复制代码
package main

type AdidasShoe struct {
	logo string
	size int
}

func (a *AdidasShoe) setLogo(logo string) {
	a.logo = logo
}

func (a *AdidasShoe) getLogo() string {
	return a.logo
}

func (a *AdidasShoe) setSize(size int) {
	a.size = size
}

func (a *AdidasShoe) getSize() int {
	return a.size
}

nikeHat.go

go 复制代码
package main

type NikeHat struct {
	logo  string
	color string
}

func (n *NikeHat) setLogo(logo string) {
	n.logo = logo
}

func (n *NikeHat) getLogo() string {
	return n.logo
}

func (n *NikeHat) setColor(color string) {
	n.color = color
}

func (n *NikeHat) getColor() string {
	return n.color
}

nikeShoe.go

go 复制代码
package main

type NikesShoe struct {
	logo string
	size int
}

func (n *NikesShoe) setLogo(logo string) {
	n.logo = logo
}

func (n *NikesShoe) getLogo() string {
	return n.logo
}

func (n *NikesShoe) setSize(size int) {
	n.size = size
}

func (n *NikesShoe) getSize() int {
	return n.size
}

抽象工厂

go 复制代码
package main

type ISportsFactory interface {
	makeShoe() IShoe
	makeHat() IHat
}

func GetSportsFactory(brand string) ISportsFactory {
	if brand == "adidas" {
		return &AdidasFactory{}
	}

	if brand == "nike" {
		return &NikeFactory{}
	}

	return nil
}

具体工厂

adidasFactory.go

go 复制代码
package main

type AdidasFactory struct {
}

func (a *AdidasFactory) makeShoe() IShoe {
	return &AdidasShoe{
		logo: "adidas",
		size: 42,
	}
}

func (a *AdidasFactory) makeHat() IHat {
	return &AdidasHat{
		logo:  "adidas",
		color: "blue",
	}
}

nikeFactory.go

go 复制代码
package main

type NikeFactory struct {
}

func (n *NikeFactory) makeShoe() IShoe {
	return &NikesShoe{
		logo: "nike",
		size: 42,
	}
}

func (n *NikeFactory) makeHat() IHat {
	return &AdidasHat{
		logo:  "nike",
		color: "red",
	}
}

客户端

client.go

go 复制代码
package main

import "fmt"

func main() {
	f := GetSportsFactory("nike")
	nikeshoe := f.makeShoe()
	fmt.Println(nikeshoe.getLogo())
	fmt.Println(nikeshoe.getSize())
	nikehat := f.makeHat()
	fmt.Println(nikehat.getLogo())
	fmt.Println(nikehat.getColor())
}

这个案例生产了鞋子、帽子,假如鞋子又分好几种,帽子也分好几种,该如何设计代码结构。

案例2

场景:

根据参数设置创建mq和storage

mq有kafka,pulsar

storage有local,minio

跟上一个例子有点不一样,这里只有一个种类,类似于只有nike品牌,nike的鞋子又有2种。

抽象产品接口:

go 复制代码
type ChunkManager interface {
	Upload()
	Download()
}

type MsgStream interface {
	Produce()
	Consume()
}

具体产品:

go 复制代码
type KafkaStream struct {
}

func (k *KafkaStream) Produce() {
	fmt.Println("Kafka produce")
}

func (k *KafkaStream) Consume() {
	fmt.Println("Kafka Consume")
}

type PulsarStream struct {
}

func (p *PulsarStream) Produce() {
	fmt.Println("Pulsar produce")
}

func (p *PulsarStream) Consume() {
	fmt.Println("Pulsar Consume")
}

每个产品类别的工厂

chunk:

go 复制代码
type ChunkFactory interface {
	NewChunk() ChunkManager
}

type ChunkFactoryImpl struct {
	chunkType string
}

func (ck *ChunkFactoryImpl) NewChunk() ChunkManager {
	if ck.chunkType == "local" {
		return &LocalChunkManager{}
	}
	if ck.chunkType == "minio" {
		return &MinioChunkManager{}
	}
	return nil
}

mq:

go 复制代码
type Mqfactory interface {
	NewMQ() MsgStream
}

type MqfactoryImpl struct {
	mqType string
}

func (mq *MqfactoryImpl) NewMQ() MsgStream {
	if mq.mqType == "kafka" {
		return &KafkaStream{}
	}
	if mq.mqType == "pulsar" {
		return &PulsarStream{}
	}
	return nil
}

抽象工厂

go 复制代码
type Factory interface {
	Init(mqType string, chunkType string)
	MakeMq() MsgStream
	MakeChunk() ChunkManager
}

具体工厂

因为只有一个类别,就用defaultFactory命名

go 复制代码
type DefaultFactory struct {
	chunkFactory ChunkFactory
	mqfactory    Mqfactory
}

func NewDefaultFactory() Factory{
	return &DefaultFactory{}
}

func (d *DefaultFactory) Init(mqType string, chunkType string) {
	d.chunkFactory = &ChunkFactoryImpl{
		chunkType: chunkType,
	}
	d.mqfactory = &MqfactoryImpl{
		mqType: mqType,
	}
}

func (d *DefaultFactory) MakeMq() MsgStream {
	return d.mqfactory.NewMQ()
}

func (d *DefaultFactory) MakeChunk() ChunkManager {
	return d.chunkFactory.NewChunk()
}

客户端:

go 复制代码
func main() {
	f := NewDefaultFactory()
	f.Init("kafka", "minio")
	k := f.MakeMq()
	k.Produce()
	m := f.MakeChunk()
	m.Upload()
}
相关推荐
LeonNo115 小时前
golang , chan学习
开发语言·学习·golang
龙门吹雪5 小时前
GO语言基础面试题
golang·面试题·map·channel·
zyh_0305215 小时前
GIN中间件
后端·golang·gin
lxyzcm6 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千6 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)6 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲8 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉12 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~13 小时前
框架专题:设计模式
设计模式·框架
先睡13 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式