Abstract Factory 抽象工厂模式简介与 C# 示例【创建型】

news2024/11/29 7:52:18

〇、简介

1、什么是抽象工厂模式?

一句话解释:

  提供一个接口,以创建一系列相关或相互依赖的抽象对象,而无需指定它们具体的类。(将一系列抽象类装进接口,一次接口实现,就必须实例化这一系列抽象类)

抽象工厂模式(Abstract Factory Pattern)是一种创建型模式。它用于创建一组相关对象的家族。强调的是一组对象之间的协作关系,而不是单个对象之间的依赖关系。抽象工厂类负责创建整个家族的对象的生命周期,并隐藏与实现有关的逻辑。

 一个比喻:(科目与课代表)

  语文和数学的课代表和副课代表,都按照抽象方法标准选好了,接下来同样的通过实现抽象类和接口标准,来选出两名物理课代表。当然,已经选出来的其他课代表,和本次选举无关联。

2、优缺点和使用场景

优点:

  • 可以降低系统中各个对象之间的耦合度。
  • 隔离了具体类的生产,使得客户并不需要知道什么被创建。
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:

  • 在增加新的产品方面比较困难,需要修改抽象工厂的接口,这样会导致所有的具体工厂也需要做出相应的修改。
  • 抽象程度高,可能会导致一些底层实现细节难以控制。

总之,抽象工厂模式能够有效地封装对象创建,但是扩展产品较为困难。它在软件开发中被广泛使用,特别是在跨平台软件开发中经常用到,使用时要注意系统对象的特点合理使用。

适用场景:

  • 一个系统要独立于它的产品的创建、组合和表示。
  • 一个系统要有多个产品系列中的一个来配置。
  • 要强调一系列相关的产品对象的设计,以便进行联合使用。
  • 提供一个产品类库,但只想显示它们的接口而不是实现。

实际使用场景举例:

  • 游戏开发:游戏中可能需要多种角色、武器、敌人等元素,它们之间可能存在关联性或依赖性,可以使用抽象工厂方法来快速构建游戏元素。
  • 数据库访问组件设计:不同数据库的连接、查询和数据存储方式可能存在差异,可以使用抽象工厂方法来创建不同数据库的访问组件、驱动和映射器。
  • 操作系统界面设计:不同操作系统的界面设计具有不同的特点,可以使用抽象工厂方法来创建不同操作系统下的控件。

总之,使用抽象工厂模式,都需要保证对象家族之间高内聚、松耦合,使得系统的设计和实现更加灵活和可扩展。

回到顶部

一、抽象工厂模式简单实现与扩展

通过两个抽象类 ProductA/ProductBBBB,实现四个具体产品类;在通过接口 IAbstractFactory,实现两个具体工厂的产品族 ConcreteFactory1/ConcreteFactory2。最后通过 Client 类注入工厂类的同时,创建产品的不同产品的实例,使客户端不用了解产品如何实例化,可以直接引用。

 
// 抽象产品类。
public abstract class ProductA
{
public abstract void OperationA();
}
public abstract class ProductBBBB
{
public abstract void OperationBBBB();
}
// 具体产品类,其中 ProductA1、ProductA2、ProductB1 和 ProductB2 分别代表不同的产品。
public class ProductA1 : ProductA
{
public override void OperationA()
{
Console.WriteLine("ProductA1's operation.");
}
}
public class ProductA2 : ProductA
{
public override void OperationA()
{
Console.WriteLine("ProductA2's operation.");
}
}
public class ProductBBBB1 : ProductBBBB
{
public override void OperationBBBB()
{
Console.WriteLine("ProductBBBB1's operation.");
}
}
public class ProductBBBB2 : ProductBBBB
{
public override void OperationBBBB()
{
Console.WriteLine("ProductBBBB2's operation.");
}
}
// 抽象工厂接口,定义了各种不同产品族的生产方法。
public interface IAbstractFactory
{
ProductA CreateProductA();
ProductBBBB CreateProductBBBB();
}
// 每个具体工厂都能够生产特定的产品族。
public class ConcreteFactory1 : IAbstractFactory
{
public ProductA CreateProductA()
{
return new ProductA1();
}
public ProductBBBB CreateProductBBBB()
{
return new ProductBBBB1();
}
}
public class ConcreteFactory2 : IAbstractFactory
{
public ProductA CreateProductA()
{
return new ProductA2();
}
public ProductBBBB CreateProductBBBB()
{
return new ProductBBBB2();
}
}
// 客户端代码使用抽象工厂来创建各种不同产品族的产品,而无需关心它们的实际实现。
public class Client
{
private readonly ProductA _productA;
private readonly ProductBBBB _productBBBB;
public Client(IAbstractFactory factory)
{
_productA = factory.CreateProductA();
_productBBBB = factory.CreateProductBBBB();
}
public void Run()
{
_productA.OperationA();
_productBBBB.OperationBBBB();
}
}
 
// 测试
static void Main(string[] args)
{
Client client = new Client(new ConcreteFactory1());
client.Run();
Client client2 = new Client(new ConcreteFactory2());
client2.Run();
// 输出:
// ProductA1's operation.
// ProductBBBB1's operation.
// ProductA2's operation.
// ProductBBBB2's operation.
}

下面我们尝试扩展出来一个新的产品 3:

 
// 具体产品类
public class ProductA3 : ProductA
{
public override void OperationA()
{
Console.WriteLine("ProductA3's operation.");
}
}
public class ProductBBBB3 : ProductBBBB
{
public override void OperationBBBB()
{
Console.WriteLine("ProductBBBB3's operation.");
}
}
// 具体工厂都能够生产特定的产品族
public class ConcreteFactory3 : IAbstractFactory
{
public ProductA CreateProductA()
{
return new ProductA3();
}
public ProductBBBB CreateProductBBBB()
{
return new ProductBBBB3();
}
}

 测试:

 
static void Main(string[] args)
{
Client client = new Client(new ConcreteFactory1());
client.Run();
Client client2 = new Client(new ConcreteFactory2());
client2.Run();
Client client3 = new Client(new ConcreteFactory3());
client3.Run();
}

  

回到顶部

二、抽象工厂模式的结构

根据上一章节的示例代码,简单画一个 UML 图,如下:

  • AbstractFactory:声明一个创建抽象产品对象的操作接口。
  • ConcreteFactory:实现创建具体产品对象的操作。
  • AbstractProduct:为一类产品对象声明一个接口。
  • ConcreteProduct:定义一个将被相应的具体工厂创建的产品对象。实现了 IAbstractProduct 接口。
  • Client:仅使用由 AbstractFactory 和 AbstractProduct 类声明的接口。

通常在运行时创建一个 ConcreteFactory 类的实例,此实例具有 AbstractFactory  中全部定义的实现。当客户端需要创建不同的产品对象时,可以通过实现抽象工厂来创建具体工厂。

AbstractFactory  将产品对象的创建延迟到它的 ConcreteFactory 子类。

回到顶部

三、抽象工厂模式在 .net 框架中的实际应用

例如 DbProviderFactory,这个类位于 System.Data.Common.dll 程序集中,该类扮演抽象工厂模式中抽象工厂的角色,源码如下:

 
// System.Data.Common, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Data.Common.DbProviderFactory
using System.Data.Common;
public abstract class DbProviderFactory
{
private bool? _canCreateDataAdapter;
private bool? _canCreateCommandBuilder;
public virtual bool CanCreateDataSourceEnumerator => false;
public virtual bool CanCreateDataAdapter
{
get
{
if (!_canCreateDataAdapter.HasValue)
{
using DbDataAdapter dbDataAdapter = CreateDataAdapter();
_canCreateDataAdapter = dbDataAdapter != null;
}
return _canCreateDataAdapter.Value;
}
}
public virtual bool CanCreateCommandBuilder
{
get
{
if (!_canCreateCommandBuilder.HasValue)
{
using DbCommandBuilder dbCommandBuilder = CreateCommandBuilder();
_canCreateCommandBuilder = dbCommandBuilder != null;
}
return _canCreateCommandBuilder.Value;
}
}
public virtual DbCommand? CreateCommand()
{
return null;
}
public virtual DbCommandBuilder? CreateCommandBuilder()
{
return null;
}
public virtual DbConnection? CreateConnection()
{
return null;
}
public virtual DbConnectionStringBuilder? CreateConnectionStringBuilder()
{
return null;
}
public virtual DbDataAdapter? CreateDataAdapter()
{
return null;
}
public virtual DbParameter? CreateParameter()
{
return null;
}
public virtual DbDataSourceEnumerator? CreateDataSourceEnumerator()
{
return null;
}
}

下面是 SqlClientFactory.cs,继承了抽象类 DbProviderFactory,需要注意的是,此为引用程序集,即只包含元数据,不含可执行代码。如何通过工厂模式访问 SQLServer 数据库,可以参考官网示例: 获取 DbProviderFactory

点击查看 SqlClientFactory.cs

下面再看一下 Oracle 工厂的实现,完全独立于其他数据库的工厂:

点击查看 OracleClientFactory.cs

当然,诸如 Mysql、DB2 等类同。由此可见,当后续新增数据库时,只需对 DbProviderFactory 抽象工厂进行继承即可,对已实现的数据工厂毫无影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1052220.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在Ubuntu中切换root用户和普通用户

问题 大家在新装Ubuntu之后,有没有发现自己进入不了root用户,su root后输入密码根本进入不了,这怎么回事呢? 打开Ubuntu命令终端; 输入命令:su root; 回车提示输入密码; 提示&…

[ubuntu]ubuntu设置虚拟内存

首先查看自己是否加过虚拟内存或者查看虚拟内存当前状态可以命令: free -mh 创建交换分区: sudo mkdir /swap cd /swap sudo dd if/dev/zero ofswapfile bs1024 count12582912 其中count是自己分配内存大小,上面为12GB,你可…

视频异常检测:Human Kinematics-inspired Skeleton-based Video Anomaly Detection

论文作者:Jian Xiao,Tianyuan Liu,Genlin Ji 作者单位:Nanjing Normal University;The Hong Kong Polytechnic University 论文链接:http://arxiv.org/abs/2309.15662v1 内容简介: 1)方向:视频异常检测…

Opengl之混合

OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。透明就是说一个物体(或者其中的一部分)不是纯色(Solid Color)的,它的颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。一个有色玻璃窗是一个透明的物体,玻璃有它自己的颜色,但它最终的…

rtp流广播吸顶喇叭网络有源吸顶喇叭

SIP-7043 rtp流广播吸顶喇叭网络有源吸顶喇叭 一、描述 SIP-7043是我司的一款SIP网络有源吸顶喇叭,具有10/100M以太网接口,内置有一个高品质扬声器,将网络音源通过自带的功放和喇叭输出播放,可达到功率20W。SIP-7043作为SIP系统的…

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录 前言技术回顾准备工作申请accessKey\secretKey创建数据存储桶公共资源直接访问测试 接入springboot实现文件服务依赖引入配置文件MinIO配置MinIO工具类 OkHttpSSLSocketClient兼容ssl静态资源预览解决方案资源上传预览测试测试结果 前言 上篇博客我们介绍了分布式云存…

四川玖璨电子商务有限公司提供专业的抖音培训课程

在今天的数字时代,抖音等社交媒体平台已经成为企业和个人扩大影响力、提高品牌知名度的主要战场。然而,如何在繁杂的社交媒体市场中取得成功呢?四川玖璨电子商务有限公司为你提供了一个解决方案——高质量的抖音培训课程。 四川玖璨电子商务…

黑马程序员Docker快速入门到项目部署(学习笔记)

目录 一、Docker简介 二、安装Docker 2.1、卸载旧版 2.2、配置Docker的yum库 2.3、安装Docker 2.4、启动和校验 2.5、配置镜像加速 2.5.1、注册阿里云账号 2.5.2、开通镜像服务 2.5.3、配置镜像加速 三、快速入门 3.1、部署MYSQL 3.2、命令解读 四、Docker基础 …

PostMan的学习

PostMan的学习 目录 环境变量和全局变量接口关联内置动态参数以及自定义动态参数实现业务闭环Postman断言批量运行collection数据驱动之CSV文件和JSON文件测试必须带请求头的接口Mock Serviers 服务器Cookie鉴权NewmanPostManNewManjenkins实现接口测试持续集成 参考资料&am…

在线文档生成:Swagger

1 简介 现在的项目开发都是前后端分离,前端和后端是两拨人在开发,所以这就涉及到前后端人员的接口交互了。如果使用自己维护的接口文档或口口相传的话,很容易出现接口更新不及时的问题。这个时候就需要像Swagger这样的在线文档生成框架出马了…

300元左右的耳机哪个性价比最好、好用的开放式耳机推荐

作为经常使用蓝牙耳机的小伙伴们而言,入耳式耳机戴久了会带来不舒适的感觉,开放式耳机显然是最舒服的选择,不入耳不会产生异物感,在户外运动可以时刻保持与外界连接更安全,也不会因为耳塞的卫生问题造成耳道感染&#…

嵌入式学习笔记(35)外部中断

6.9.1什么是外部中断 (1)内部中断就是指中断源来自于SoC内部(一般是内部外设),譬如串口、定时器等部件产生的中断;外部中断是SoC外部的设备,通过外部中断对应的GPIO引脚产生的中断。 (2)按键在SoC中就使用了外部中断…

【MySQL】数据类型(二)

文章目录 一. char字符串类型二. varchar字符串类型2.1 char和varchar比较 三. 日期和时间类型四. enum和set类型4.1 set的查询 结束语 一. char字符串类型 char (L) 固定长度字符串 L是可以存储的长度,单位是字符,最大长度是255 MySQL中的字符&#xff…

No159.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

Kafka(一)使用Docker Compose安装单机Kafka以及Kafka UI

文章目录 Kafka中涉及到的术语Kafka镜像选择Kafka UI镜像选择Docker Compose文件Kafka配置项说明KRaft vs Zookeeper和KRaft有关的配置关于Controller和Broker的概念解释Listener的各种配置 Kafka UI配置项说明 测试Kafka集群Docker Compose示例配置 Kafka中涉及到的术语 对于…

基于SpringBoot的美发门店管理系统

目录 前言 一、技术栈 二、系统功能介绍 美容项目管理 产品库存管理 产品购买管理 会员卡管理 项目预定管理 产品购买信息 会员充值管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用,管理信…

笔试强训Day8

链接&#xff1a;求最小公倍数__牛客网 T1:求最小公倍数 正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值&#xff0c;设计一个算法&#xff0c;求输入A和B的最小公倍数。 数据范围&#xff1a;1≤a,b≤100000 #include<iostream> using namespace…

第四章 c数组

数组的概念 若干个相同类型的变量在内存中有序存储的集合。 数组在内存中是连续存储的。 数组的分类 按元素的类型分类 char a[10]; short a[10]; int a[10]; long int a[10]; int *a[10]; struct stu boy[10];按维度分类 int a[10]; int b[1][2]; int c[1][2][3]; int d[…

win10打开VMware 16 pro里面的虚拟机就蓝屏怎么办

2023年9月30日&#xff0c;周六下午 今天下午我也遇到了这个问题&#xff0c;后来解决了&#xff0c;于是记录一下我的解决办法 目录 1、打开控制面板&#xff0c;并选择“程序和功能” 2、点击“启动或关闭Windows服务” 3、勾选两个服务 4、重启电脑&#xff0c;大功告成…

博弈论中静态博弈经典场景案例

博弈论中静态博弈经典场景案例 1、齐威王田忌赛马 田忌赛马是中国家喻户晓的故事&#xff0c;故事讲述的是齐国大将田忌的谋士孙膑如何运用计谋帮助田忌在与齐威王赛马时以弱胜强的故事&#xff0c;这个故事其实本质也是一个博弈的过程。     齐威王要和田忌赛马&#xff…