C# 中的抽象工厂模式傻瓜教程






3.44/5 (7投票s)
这就是像我这样的傻瓜解释抽象工厂模式以及如何使用它的方法
引言
如果您是一位经验丰富的 C#/Java 应用程序开发人员,您肯定已经使用过抽象工厂模式。如果您还没有……不用担心,这个菜鸟在这里为您效劳。在我刚开始编码生涯时,我并没有意识到这个模式的重要性,但幸运的是,我的老板兼项目架构师在特定情况下使用某些设计模式方面非常讲究。所以,经过一番自我鞭策,我终于理解了,更重要的是,获得了关于设计模式的“何时使用”的意识。
在所有设计模式中,这可以说是最重要也最常用的一个。在继续之前,我假设您作为读者知道 OOPS 中的抽象类是什么。如果您对此不太熟悉,请自己去谷歌搜索一下。
背景
当尝试在代码中解决某些架构问题时,设计模式就能派上用场。例如,假设您的应用程序有 3 种不同的数据源 - Web 服务、数据库和 Excel 导入。现在,在编码时,作为开发人员,您需要决定是为每种数据源编写单独的代码,还是有一种方法可以从应用程序的更高层抽象出数据库类型,这样应用程序的更高层(业务逻辑/UI 层)就不必担心底层(数据层)是否已更改了?
因此,我们今天要使用的模式是一种专门解决此类问题的方法,它通过隔离和抽象应用程序其他部分的不同“类型”的数据库。
让我们来探索一下代码的使用
那么“抽象工厂模式”做什么呢?
- 它提供了一个通用的访问点。
- 它隐藏了类型细节,在本例中是数据库类型,使其与应用程序的其余部分隔离开。
让我们尝试阐明第二点:所以,在我们的示例中,我们不会直接处理 3 个不同的连接类:1. SQLConnection
2. ExcelConnection
3. WebServiceConnection
,而是只有一个连接会向应用程序层可见,即 DataConnection 类。
问题陈述和理论解决方案
我们有两个数据库,一个是 Sql Server,另一个是 Ole Db。现在我们想使用抽象工厂模式,以便在连接更改时,我们的应用程序层不必有单独的实现。所有东西都将抽象在数据层本身。
要使用此模式,我们必须遵循 3 个基本步骤:
- 创建一个具有通用属性/行为的抽象类。
- 将抽象类继承到具有实现细节的其他类中。
- 使用工厂模式获取抽象类型并执行具体操作。
步骤 1:创建一个具有通用属性/行为的抽象类。
首先,让我们创建我们的抽象类。所以我们的 AbstractDatabase 抽象类有两个属性:
- Connection 属性 (System.Data.Common.DBConnection)
- Command 属性 (System.Data.Common.DBCommand)
public abstract class AbstractDatabase
{
public virtual DbConnection Connection { get; set; }
public virtual DbCommand Command { get; set; }
}
步骤 2:将抽象类继承到具有实现细节的其他类中。
现在我们知道不能实例化抽象类。实例类将继承它。
因此,对于我们的 SQLServerDatabase
类,我们将继承自 Database 抽象类,Connection 属性将返回 SqlConnection
,Command 属性将返回 SqlCommand。
下面的代码片段只是实例化了 SQLConnection 的连接和命令。
public class SqlServerDatabase : AbstractDatabase
{
private DbConnection _connection = null;
private DbCommand _command = null;
public override DbConnection Connection
{
get
{
if(_connection == null)
{
_connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SqlServerConnection"].ConnectionString);
}
return _connection;
}
set
{
_connection = value;
}
}
public override DbCommand Command
{
get
{
if(_command == null)
{
_command = new SqlCommand();
_command.Connection = Connection;
}
return _command;
}
set
{
_command = value;
}
}
现在假设我们也有一个 OleDB Connection 数据源。为此,我们将创建一个新类 OleDBDatabase
。它将继承并实现我们的抽象类。这里的 Connection 属性将返回 OleDBConnection
,Command 属性将返回 OleDBCommand
。
与上述相同,下面的代码片段仅实例化了 OleDbConnection 的连接和命令。
public class OleDbDatabase : AbstractDatabase
{
private DbConnection _connection = null;
private DbCommand _command = null;
public override DbConnection Connection
{
get
{
if(_connection == null)
{
_connection = new OleDbConnection(ConfigurationManager.ConnectionStrings["OleDbServerConnection"].ConnectionString);
}
return _connection;
}
set
{
_connection = value;
}
}
public override DbCommand Command
{
get
{
if (_command == null)
{
_command = new OleDbCommand();
_command.Connection = Connection;
}
return _command;
}
set
{
_command = value;
}
}
}
步骤 3:使用工厂模式获取抽象类型并执行具体操作。
我们来到了实现我们模式的最后一步。那么,它如何帮助我们隐藏应用程序其余部分的细节呢?
这个问题的答案是——应用程序的其余部分不直接处理 OleDBDatabase
或 SQLServerDatabase
类。相反,它处理抽象类,在这个过程中,工厂模式将帮助我们找到正确的实现。
为了演示这一点,我创建了一个小的 Forms 应用程序,它有两个单选按钮。所以窍门是,无论您选择哪个单选按钮,它都将只返回该实例。
private void btnSelect_Click(object sender, EventArgs e)
{
AbstractDatabase database;
if (radSqlServer.Checked)
{
database = new OleDbDatabase();
}
else
{
database = new SqlServerDatabase();
}
//Use Connection methods as you want it
}
让我们讨论上面的代码片段:在 button click
事件中,我们选择了数据库类型。数据库类型在这里是抽象的。因此,当我们的应用程序的更高层处理数据库实例时,它不需要知道它是在处理 OleDb 还是 SqlServer。它只会处理 AbstractDatabase
类型。您不需要费力去理解这里发生了什么。与使用具体类型不同,我们只是使用抽象类型来隐藏应用程序其他部分的实现细节。
注意: 我上传了一个解决方案文件供您参考。它包含与本文讨论的代码基本相同的代码。在使用解决方案文件时,请在 app.config 文件中添加您的正确连接字符串。
实际应用
所以,如果您正在开发一个应用程序,该应用程序具有:
1. 多个数据源。
2. 多个输出文件格式 - 例如,您必须将报告输出到 PDF、Excel、Word 等。但您不希望应用程序的其他部分知道您是如何做到的。
3. 任何具有多个输入/输出类型但可以将其概括为一个的场景。
相对于具体实现的优势
为了理解抽象工厂模式的优势,让我用我们自己的例子给您一个场景。假设明天有一个新版本的 Oracle 数据源出现,而您那个疯狂的客户希望在我们的应用程序中使用它。到时候您会怎么做……您会重写整个数据层应用程序层来适应更改吗?
如果您使用了抽象工厂模式,您的答案将是否定的。您只需创建一个新的具体类,该类继承自 Abstract database 类型,并根据选择实例化 OracleDataClient,这样您就可以继续了。
祝大家编码愉快。:) 很乐意回答您的问题