设计模式(四)抽象工厂模式(Abstract Factory Pattern)
一、引言
在上一专题中介绍了工厂方法模式,工厂方法模式是为了克服简单工厂模式的缺点而设计出来的,简单工厂模式的工厂类随着产品类的增加需要增加额外的代码,而工厂方法模式每个具体工厂类只完成单个实例的创建,所以它具有很好的可扩展性。但是在现实生活中,一个工厂只创建单个产品这样的例子很少,因为现在的工厂都多元化了,一个工厂创建一系列的产品,如果我们要设计这样的系统时,工厂方法模式显然在这里不适用,然后抽象工厂模式却可以很好地解决一系列产品创建的问题,这是本专题所要介绍的内容。
二、抽象工厂详细介绍
这里首先以一个生活中抽象工厂的例子来实现一个抽象工厂,然后再给出抽象工厂的定义和UML图来帮助大家更好地掌握抽象工厂模式,同时大家在理解的时候,可以对照抽象工厂生活中例子的实现和它的定义来加深抽象工厂的UML图理解。
2.1 抽象工厂的具体实现
下面就以生活中 “绝味” 连锁店的例子来实现一个抽象工厂模式。例如,绝味鸭脖想在江西南昌和上海开分店,但是由于当地人的口味不一样,在南昌的所有绝味的东西会做的辣一点,而上海不喜欢吃辣的,所以上海的所有绝味的东西都不会做的像南昌的那样辣,然而这点不同导致南昌绝味工厂和上海的绝味工厂生成所有绝味的产品都不同,也就是某个具体工厂需要负责一系列产品(指的是绝味所有食物)的创建工作,下面就具体看看如何使用抽象工厂模式来实现这种情况。
/// <summary>
/// 下面以绝味鸭脖连锁店为例子演示下抽象工厂模式
/// 因为每个地方的喜欢的口味不一样,有些地方喜欢辣点的,有些地方喜欢吃不辣点
/// 客户端调用
/// </summary>
class Client
{
static void Main(string[] args)
{
// 南昌工厂制作南昌的鸭脖和鸭架
AbstractFactory nanChangFactory = new NanChangFactory();
YaBo nanChangYabo = nanChangFactory.CreateYaBo();
nanChangYabo.Print();
YaJia nanChangYajia= nanChangFactory.CreateYaJia();
nanChangYajia.Print();
// 上海工厂制作上海的鸭脖和鸭架
AbstractFactory shangHaiFactory = new ShangHaiFactory();
shangHaiFactory.CreateYaBo().Print();
shangHaiFactory.CreateYaJia().Print();
Console.Read();
}
}
/// <summary>
/// 抽象工厂类,提供创建两个不同地方的鸭架和鸭脖的接口
/// </summary>
public abstract class AbstractFactory
{
// 抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了绝味中鸭脖和鸭架的创建接口
public abstract YaBo CreateYaBo();
public abstract YaJia CreateYaJia();
}
/// <summary>
/// 南昌绝味工厂负责制作南昌的鸭脖和鸭架
/// </summary>
public class NanChangFactory : AbstractFactory
{
// 制作南昌鸭脖
public override YaBo CreateYaBo()
{
return new NanChangYaBo();
}
// 制作南昌鸭架
public override YaJia CreateYaJia()
{
return new NanChangYaJia();
}
}
/// <summary>
/// 上海绝味工厂负责制作上海的鸭脖和鸭架
/// </summary>
public class ShangHaiFactory : AbstractFactory
{
// 制作上海鸭脖
public override YaBo CreateYaBo()
{
return new ShangHaiYaBo();
}
// 制作上海鸭架
public override YaJia CreateYaJia()
{
return new ShangHaiYaJia();
}
}
/// <summary>
/// 鸭脖抽象类,供每个地方的鸭脖类继承
/// </summary>
public abstract class YaBo
{
/// <summary>
/// 打印方法,用于输出信息
/// </summary>
public abstract void Print();
}
/// <summary>
/// 鸭架抽象类,供每个地方的鸭架类继承
/// </summary>
public abstract class YaJia
{
/// <summary>
/// 打印方法,用于输出信息
/// </summary>
public abstract void Print();
}
/// <summary>
/// 南昌的鸭脖类,因为江西人喜欢吃辣的,所以南昌的鸭脖稍微会比上海做的辣
/// </summary>
public class NanChangYaBo : YaBo
{
public override void Print()
{
Console.WriteLine("南昌的鸭脖");
}
}
/// <summary>
/// 上海的鸭脖没有南昌的鸭脖做的辣
/// </summary>
public class ShangHaiYaBo : YaBo
{
public override void Print()
{
Console.WriteLine("上海的鸭脖");
}
}
/// <summary>
/// 南昌的鸭架
/// </summary>
public class NanChangYaJia : YaJia
{
public override void Print()
{
Console.WriteLine("南昌的鸭架子");
}
}
/// <summary>
/// 上海的鸭架
/// </summary>
public class ShangHaiYaJia : YaJia
{
public override void Print()
{
Console.WriteLine("上海的鸭架子");
}
}
2.2 抽象工厂模式的定义和类图
上面代码中都有详细的注释,这里就不再解释上面的代码了,下面就具体看看抽象工厂模式的定义吧(理解定义可以参考上面的实现来加深理解):
抽象工厂模式:提供一个创建产品的接口来负责创建相关或依赖的对象,而不具体明确指定具体类
抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么。这样客户就可以从具体产品中被解耦。下面通过抽象工模式的类图来了解各个类中之间的关系:

2.3 抽象工厂应对需求变更
看完上面抽象工厂的实现之后,如果 “绝味”公司又想在湖南开一家分店怎么办呢? 因为湖南人喜欢吃麻辣的,下面就具体看看应用了抽象工厂模式的系统是如何应对这种需求的。
/// <summary>
/// 如果绝味又想开一家湖南的分店时,因为湖南喜欢吃麻的
/// 所以这是有需要有一家湖南的工厂专门制作
/// </summary>
public class HuNanFactory : AbstractFactory
{
// 制作湖南鸭脖
public override YaBo CreateYaBo()
{
return new HuNanYaBo();
}
// 制作湖南鸭架
public override YaJia CreateYaJia()
{
return new HuNanYajia();
}
}
/// <summary>
/// 湖南的鸭脖
/// </summary>
public class HuNanYaBo : YaBo
{
public override void Print()
{
Console.WriteLine("湖南的鸭脖");
}
}
/// <summary>
/// 湖南的鸭架
/// </summary>
public class HuNanYajia : YaJia
{
public override void Print()
{
Console.WriteLine("湖南的鸭架子");
}
}
此时,只需要添加三个类:一个是湖南具体工厂类,负责创建湖南口味的鸭脖和鸭架,另外两个类是具有湖南口味的鸭脖类和鸭架类。从上面代码看出,抽象工厂对于系列产品的变化支持 “开放——封闭”原则(指的是要求系统对扩展开放,对修改封闭),扩展起来非常简便,但是,抽象工厂对于添加新产品这种情况就不支持”开放——封闭 “原则,这也是抽象工厂的缺点所在,这点会在第四部分详细介绍。
三、抽象工厂的分析
抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展,这真是抽象工厂模式的优点所在,然后抽象模式同时也存在不足的地方。下面就具体看下抽象工厂的缺点(缺点其实在前面的介绍中以已经涉及了):
抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。
知道了抽象工厂的优缺点之后,也就能很好地把握什么情况下考虑使用抽象工厂模式了,下面就具体看看使用抽象工厂模式的系统应该符合那几个前提:
一个系统不要求依赖产品类实例如何被创建、组合和表达的表达,这点也是所有工厂模式应用的前提。
这个系统有多个系列产品,而系统中只消费其中某一系列产品
系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。
四、.NET中抽象工厂模式实现
抽象工厂模式在实际中的应用也是相当频繁的,然而在我们.NET类库中也存在应用抽象工厂模式的类,这个类就是System.Data.Common.DbProviderFactory,这个类位于System.Data.dll程序集中,该类扮演抽象工厂模式中抽象工厂的角色,我们可以用reflector反编译工具查看该类的实现:
/// 扮演抽象工厂的角色
/// 创建连接数据库时所需要的对象集合,
/// 这个对象集合包括有 DbConnection对象(这个是抽象产品类,如绝味例子中的YaBo类)、DbCommand类、DbDataAdapter类,针对不同的具体工厂都需要实现该抽象类中方法,
public abstract class DbProviderFactory
{
// 提供了创建具体产品的接口方法
protected DbProviderFactory();
public virtual DbCommand CreateCommand();
public virtual DbCommandBuilder CreateCommandBuilder();
public virtual DbConnection CreateConnection();
public virtual DbConnectionStringBuilder CreateConnectionStringBuilder();
public virtual DbDataAdapter CreateDataAdapter();
public virtual DbDataSourceEnumerator CreateDataSourceEnumerator();
public virtual DbParameter CreateParameter();
public virtual CodeAccessPermission CreatePermission(PermissionState state);
}
DbProviderFactory类是一个抽象工厂类,该类提供了创建数据库连接时所需要的对象集合的接口,实际创建的工作在其子类工厂中进行,微软使用的是SQL Server数据库,因此提供了连接SQL Server数据的具体工厂实现,具体代码可以用反编译工具查看,具体代码如下:
/// 扮演着具体工厂的角色,用来创建连接SQL Server数据所需要的对象
public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider
{
// Fields
public static readonly SqlClientFactory Instance = new SqlClientFactory();
// 构造函数
private SqlClientFactory()
{
} // 重写抽象工厂中的方法
public override DbCommand CreateCommand()
{ // 创建具体产品
return new SqlCommand();
}
public override DbCommandBuilder CreateCommandBuilder()
{
return new SqlCommandBuilder();
}
public override DbConnection CreateConnection()
{
return new SqlConnection();
}
public override DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return new SqlConnectionStringBuilder();
}
public override DbDataAdapter CreateDataAdapter()
{
return new SqlDataAdapter();
}
public override DbDataSourceEnumerator CreateDataSourceEnumerator()
{
return SqlDataSourceEnumerator.Instance;
}
public override DbParameter CreateParameter()
{
return new SqlParameter();
}
public override CodeAccessPermission CreatePermission(PermissionState state)
{
return new SqlClientPermission(state);
}
}
因为微软只给出了连接SQL Server的具体工厂的实现,我们也可以自定义连接Oracle、MySql的具体工厂的实现。
五、总结
到这里,抽象工厂模式的介绍就结束,在下一专题就将为大家介绍建造模式。
以上内容摘抄自http://learninghard.blog.51cto.com/6146675/1297704
设计模式(四)抽象工厂模式(Abstract Factory Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern)
原文:乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factor ...
- 二十四种设计模式:抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式(Abstract Factory Pattern) 介绍提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 示例有Message和MessageModel,Messag ...
- 【设计模式】抽象工厂模式 Abstract Factory Pattern
简单工厂模式是一个工厂类根据工厂方法的参数创建不出不同的产品, 工厂方法模式是每一个产品都有一个一一对应的工厂负责创建该产品.那么今天要讲的抽象工厂模式是一个工厂能够产生关联的一系列产品.抽象工厂模式 ...
- 【UE4 设计模式】抽象工厂模式 Abstract Factory Pattern
概述 描述 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类:具体的工厂负责实现具体的产品实例 抽象工厂中每个工厂可以创建多种产品(如苹果公司生产iPhone.iPad): 工厂方法 ...
- 设计模式之抽象工厂模式(Abstract Factory Pattern)
一.抽象工厂模式的由来 抽象工厂模式,最开始是为了解决操作系统按钮和窗体风格,而产生的一种设计模式.例如:在windows系统中,我们要用windows设定的按钮和窗体,当我们切换Linux系统时,要 ...
- 设计模式 - 抽象工厂模式(abstract factory pattern) 具体解释
抽象工厂模式(abstract factory pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/2709 ...
- Net设计模式实例之抽象工厂模式(Abstract Factory Pattern)
一.抽象工厂模式简介(Bref Introduction) 抽象工厂模式(Abstract Factory Pattern),提供一个创建一系列相关或者相互依赖对象的接口,而无需制定他们的具体类.优点 ...
- C#设计模式——抽象工厂模式(Abstract Factory Pattern)
一.概述在软件开发中,常常会需要创建一系列相互依赖的对象,同时,由于需求的变化,往往存在较多系列对象的创建工作.如果采用常规的创建方法(new),会造成客户程序和对象创建工作的紧耦合.对此,抽象工厂模 ...
- 六个创建模式之抽象工厂模式(Abstract Factory Pattern)
问题: 使用工厂方法模式的主要问题是工厂类过多,每个产品对应一个工厂,不利于维护.因此可以考虑使用一个工厂创建一个产品族. 定义: 提供创建一些列相关或相互依赖的对象实例的接口,这些类可以称为一个产品 ...
- 23种设计模式之抽象工厂(Abstract Factory Pattern)
抽象工厂 当想创建一组密不可分的对象时,工厂方法似乎就不够用了 抽象工厂是应对产品族概念的.应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品.比如,每个汽车公司可能要同时生产轿车.货车. ...
随机推荐
- 夜深了,写了个JQuery的省市区三级级联效果
刚刚练手的JQuery,希望大神们指正 主要实现以下功能: 1.三级菜单级联加载数据 2.可以在不操作脚本的情况下,给元素加属性实现级联功能 3.自定义动态显示数据 咨询问题: 对于一般比较固定不变的 ...
- mina框架详解
转:http://blog.csdn.net/w13770269691/article/details/8614584 mina框架详解 分类: web2013-02-26 17:13 12651人 ...
- 实现Div拖拽
直观的理解div拖拽:当鼠标对着可拖拽部分按住后并拖动,div会跟着鼠标一起运动,并且其运动空间限制在浏览器内部,当放开鼠标时,则div停止运动. 实现div拖拽需要三个重要的事件: (1)onmou ...
- 磁盘分区在windows和linux下的表现形式对比
写在前面的话:磁盘分区在windows下面比较好理解,在linux下会有挂载的概念,理解起来比较难,但是可以通过与windows对比,以一种通俗的方式将他们梳理清楚. ====正文开始==== 我们的 ...
- ArcGIS之Cartogram地图变形记
一.地图会说谎 地图作为真实世界的抽象,是“用图说话”最可靠的工具,但是有的时候地图也会撒一些小小的谎言,其中最著名的例子当属美国总统大选.如图1是2012年美国总统大选后网上给出的一个结果图,红色代 ...
- 自动化安装SQL Server+SP就那么简单
随着业务.企业规模的日益壮大,DB的数量也在不断增多,配置一台新增DB,从服务器的参数配置,磁盘阵列规划,DB安装部署,DB参数调优等等一列步骤下来,手工操作的效率变得越来越低,因为我负责的数据库近些 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...
- Atitti 存储引擎支持的国内点与特性attilax总结
Atitti 存储引擎支持的国内点与特性attilax总结 存储引擎处理的事情: · 并发性:某些应用程序比其他应用程序具有很多的颗粒级锁定要求(如行级锁定). · 事务支持:并非所有的应用程序都需要 ...
- java.io中的System.in、 System.out和System.err
System.in, System.out, System.err这3个流同样是常见的数据来源和数据流目的地.使用最多的可能是在控制台程序里利用System.out将输出打印到控制台上. JVM启动的 ...
- 【Win10应用开发】自定义磁贴通知的排版
前面老周用了两篇烂文,向大家介绍了Adaptive磁贴的模板使用.那些XML模板已经很强大了,不过,如果你觉得那些排版还不足以满足需求,不妨试试自己来定义磁贴的内容. 其实,Runtime App支持 ...