4

Abstract Factory Pattern 抽象工厂模式简介与 C# 示例【创建型】【设计模式来了】 -...

 11 months ago
source link: https://www.cnblogs.com/czzj/p/SJMSLL_AbstractFactory.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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


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

  1868241-20230529180958994-142100166.png

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

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

// System.Data.Common, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a// System.Data.Common.DbProviderFactoryusing 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

 // System.Data.SqlClient, Version=4.6.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a// System.Data.SqlClient.SqlClientFactoryusing System.Data.Common;using System.Data.SqlClient; /// <summary>Represents a set of methods for creating instances of the <see cref="N:System.Data.SqlClient" /> provider's implementation of the data source classes.</summary>public sealed class SqlClientFactory : DbProviderFactory{ /// <summary>Gets an instance of the <see cref="T:System.Data.SqlClient.SqlClientFactory" />. This can be used to retrieve strongly typed data objects.</summary> public static readonly SqlClientFactory Instance; internal SqlClientFactory() { } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbCommand" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbCommand" />.</returns> public override DbCommand CreateCommand() { throw null; } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbCommandBuilder" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbCommandBuilder" />.</returns> public override DbCommandBuilder CreateCommandBuilder() { throw null; } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbConnection" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbConnection" />.</returns> public override DbConnection CreateConnection() { throw null; } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbConnectionStringBuilder" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbConnectionStringBuilder" />.</returns> public override DbConnectionStringBuilder CreateConnectionStringBuilder() { throw null; } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbDataAdapter" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbDataAdapter" />.</returns> public override DbDataAdapter CreateDataAdapter() { throw null; } /// <summary>Returns a strongly typed <see cref="T:System.Data.Common.DbParameter" /> instance.</summary> /// <returns>A new strongly typed instance of <see cref="T:System.Data.Common.DbParameter" />.</returns> public override DbParameter CreateParameter() { throw null; }}

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

点击查看源码 OracleClientFactory.cs

#region 程序集 Oracle.ManagedDataAccess, Version=4.122.21.1, Culture=neutral, PublicKeyToken=89b483f429c47342// C:\Users\zheng\.nuget\packages\oracle.manageddataaccess\21.10.0\lib\net462\Oracle.ManagedDataAccess.dll// Decompiled with ICSharpCode.Decompiler 7.1.0.6543#endregion using System;using System.Data.Common;using System.Security;using System.Security.Permissions;using OracleInternal.Common; namespace Oracle.ManagedDataAccess.Client{ public sealed class OracleClientFactory : DbProviderFactory { public static readonly OracleClientFactory Instance = new OracleClientFactory(); public override bool CanCreateDataSourceEnumerator => true; public override DbCommand CreateCommand() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand); } try { return new OracleCommand(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand); } } } public override DbCommandBuilder CreateCommandBuilder() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder); } try { return new OracleCommandBuilder(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder); } } } public override DbConnection CreateConnection() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection); } try { return new OracleConnection(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection); } } } public override DbConnectionStringBuilder CreateConnectionStringBuilder() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder); } try { return new OracleConnectionStringBuilder(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder); } } } public override DbDataAdapter CreateDataAdapter() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter); } try { return new OracleDataAdapter(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter); } } } public override DbDataSourceEnumerator CreateDataSourceEnumerator() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator); } try { return new OracleDataSourceEnumerator(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator); } } } public override DbParameter CreateParameter() { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter); } try { return new OracleParameter(); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter); } } } public override CodeAccessPermission CreatePermission(PermissionState state) { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission); } try { return new OraclePermission(state); } catch (Exception ex) { OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission, ex); throw; } finally { if (ProviderConfig.m_bTraceLevelPublic) { Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission); } } } }}

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK