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

package main

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

iShoe.go

package main

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

具体产品

adidasHat.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

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

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

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
}

抽象工厂

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

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

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

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种。

在这里插入图片描述

抽象产品接口:

type ChunkManager interface {
	Upload()
	Download()
}

type MsgStream interface {
	Produce()
	Consume()
}

具体产品:

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:

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:

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
}

抽象工厂

type Factory interface {
	Init(mqType string, chunkType string)
	MakeMq() MsgStream
	MakeChunk() ChunkManager
}

具体工厂

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

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()
}

客户端:

func main() {
	f := NewDefaultFactory()
	f.Init("kafka", "minio")
	k := f.MakeMq()
	k.Produce()
	m := f.MakeChunk()
	m.Upload()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shulu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值