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

news/2024/5/9 5:42:34

抽象工厂模式

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

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

抽象工厂模式(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 maintype IHat interface {setLogo(logo string)setColor(color string)getLogo() stringgetColor() string
}

iShoe.go

package maintype IShoe interface {setLogo(logo string)setSize(size int)getLogo() stringgetSize() int
}

具体产品

adidasHat.go

package maintype AdidasHat struct {logo stringcolor 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 maintype AdidasShoe struct {logo stringsize 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 maintype NikeHat struct {logo  stringcolor 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 maintype NikesShoe struct {logo stringsize 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 maintype ISportsFactory interface {makeShoe() IShoemakeHat() IHat
}func GetSportsFactory(brand string) ISportsFactory {if brand == "adidas" {return &AdidasFactory{}}if brand == "nike" {return &NikeFactory{}}return nil
}

具体工厂

adidasFactory.go

package maintype 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 maintype 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 mainimport "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() MsgStreamMakeChunk() ChunkManager
}

具体工厂

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

type DefaultFactory struct {chunkFactory ChunkFactorymqfactory    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()
}

http://www.mrgr.cn/p/40825876

相关文章

DS进阶:AVL树和红黑树

一、AVL树 1.1 AVL树的概念 二叉搜索树(BST)虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-…

WDS+MDT网络启动自动部署windows(十)硬盘分区格式化全自动

简介: 虽然WDS+MDT在单硬盘很方便,但是各种大硬盘,小固态加大硬盘的地方,还是有若干不便之处。 如:第二块硬盘未分区格式化,需要手动选择安装磁盘,如果固态超过250G,是否需要分为两个区? 自动选择较小的硬盘 目前我这里的终端都是小固态,大机械,那么我们要尝试一下自…

关于pdf.js中文本坐标尺寸的使用

一个电子教材项目中有这样一个需求: 用户向网站上传一个PDF书籍后,网站可以对PDF书籍进行解析,并支持用户对PDF书籍的每一页做一些操作,比如:为英语课本的单词和句子添加音频热区。因为热区数量很多,所以&a…

【数据库】MongoDB

文章目录 [toc]数据库操作查询数据库切换数据库查询当前数据库删除数据库查询数据库版本 数据集合操作创建数据集合查询数据集合删除数据集合 数据插入插入id重复的数据 数据更新数据更新一条丢失其他字段保留其他字段 数据批量更新 数据删除数据删除一条数据批量删除 数据查询…

Java面向对象03——三大特性之继承

一、继承(extends) 1.1、继承是什么 继承就是 Java 允许我们用 extends 关键字,让一个类与另一个类建立起一种父子关系; 被继承的类称为父类(基类、超类),继承父类的类都称为子类(派生类) ,当子类继承父类后,就可以直接使用父类公共的属性和方法了 当子类继承父类后…

使用 Docker 部署 Nuxt.js 应用程序

来源:https://medium.com/@jkpeyi/deploying-a-nuxt-js-application-with-docker-69bf822c066dWhen developing a Nuxt.js application, it’s essential to be able to deploy it easily and reproducibly. In this article, we will explore how to use Docker to deploy a …

Rust中的函数指针

什么是函数指针 通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 fn (使用小写的 ”f” )以免与 Fn 闭包 trait 相混淆。fn 被称为 函数指针(function pointer)。指定参数为函数指针的语法类似于闭包。 函数指…

Rabbitmq系列02---Exchange

个人理解: 交换机的类型划分个人理解是能过routing key来划分的,一是否按routing key找队列;fanout就是不按routingkey找队列,Direct和Topicr按routingkey找队列,只是一个模糊找,一个精准找,而headers不按routingkey 是按消头中的内容找队列。 一、Fanout(订阅模式|广播…

python之List列表

1. 高级数据类型 Python中的数据类型可以分为:数字型(基本数据类型)和非数字型(高级数据类型) 数字型包含:整型int、浮点型float、布尔型bool、复数型complex 非数字型包含:字符串str、列表l…

陈畅亮搞的专利在Windows上利用加解密DLL模块对数据库连接字符串进行加解密

陈畅亮搞的专利在Windows上利用加解密DLL模块对数据库连接字符串进行加解密这种专利权人是公司,个人是发明人,专利年费是申请人先垫付,然后公司报销了,这个专利本身就不属于员工的这个是公司是专利权人, 使用权是公司 , 如果想要维持权利的话 ,需要缴纳年费 ,专利发明…

[最新]CentOS7设置开机自启动Hadoop集群

安装好Hadoop后我们可以使用开机自启动的方式,节约敲命令的时间。注意是centOS7版本!!!和centOS6版本区别非常大!!! 1、切换到系统目录 [rootmaster ~]# cd /etc/systemd [rootmaster systemd]# ll total 32 -rw-r--r-- 1 root root 720 Jun 30 23:11 bootcha…

19-项目干系人管理(10/10 十大管理)

17.1 管理基础 17.1.1 管理的重要性 每个项目干系人,他们会受到项目积极或消极的影响,或者能对项目施加积极或消极的影响。项目经理和团队管理干系人的能力决定着项目的成败。为提高项目成功的概率,尽早开始识别干系人并引导干系人参与。 干系人满意度应作为项目目标加以识别…

基于Springboot的考研资讯平台

基于SpringbootVue的考研资讯平台的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页 考研资讯 报考指南 资料信息 论坛信息 后台登录 考研资讯管理 学生管理 资…

滴水逆向 FileBuffer-ImageBuffer 课后作业

1)- 实现如下功能 #include<stdio.h> #include<stdlib.h> #include<windows.h> BYTE* bufferApply nullptr;//将磁盘文件复制到内存中后, 使用bufferApply指向该空间 DWORD fileSize 0;//将磁盘文件复制到内存时使用需要申请空间, 使用fileSize设置申请空…

17-项目风险管理(8/10 十大管理)

15.1 管理基础 15.1.1 项目风险概述 项目风险是一种不确定的事件或条件,一旦发生,会对项目目标产生某种正面或负面的影响。项目风险既包括对项目目标的威胁,也包括促进项目目标的机会。 风险源于所有项目之中的不确定因素。项目在不同阶段会有不同的风险。风险会随着项目的进…

18-项目采购管理(9/10 十大管理)

16.1 管理基础 16.1.1 协议/采购合同 项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程。例如合同、订购单、协议备忘录(MOA)和服务水平协议(SLA)。被授权采购项目所需货物、服务的人员可以是项目团队、管理层或组织采购部的成员。 因应用领域不同…

URL路由基础与Django处理请求的过程分析

1. URL路由基础 对于高质量的Web应用来讲&#xff0c;使用简洁、优雅的URL设计模式非常有必要。Django框架允许设计人员自由地设计URL模式&#xff0c;而不用受到框架本身的约束。对于URL路由来讲&#xff0c;其主要实现了Web服务的入口。用户通过浏览器发送过来的任何请求&am…

15-项目沟通管理(7/10 十大管理)

14.1 管理基础 14.1.1 沟通 沟通是指用各种可能的方式来发送或接收信息。具体形式包括:书面行驶、口头形式、正式或非正式、手势动作、媒体行驶、遣词造句。 14.1.2 沟通模型 关键要素包括:编码:把思想或想法转化为他人能理解的语言 信息和反馈信息 媒介 噪声 解码:把信息还…

与 Apollo 共创生态:Apollo 七周年大会给带来的震撼

文章目录 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻二、Apollo 企业生态计划与开放平台2.1 Apollo X 企业自动驾驶解决方案2.2 Apollo 开放平台携手伙伴共创生态 三、个人感悟 一、七年蛰伏&#xff0c;Apollo 迎来“智变”时刻 让我们把时间倒回到 2013 年&#xff0…