一、动机(Motivation)

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?

二、意图(Intent)

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

三、结构(Structure)

ProductA1和ProductB1是一个系列,ProductA2和ProductB2是另一个系列。ConcreteFactory1是创建系列1的工厂方法,ConcreteFactory2是创建系列2的工厂方法。客户程序Client只依赖了AbstractFactory和AbstractProductA、AbstractProductB,也就是客户程序不依赖于具体实现,而是只依赖与抽象类。

如果现在需要创建一个系列3运用到客户程序,我们只需要再写一个系列3的工厂,继承自AbstractFactory,这个工厂提供了2个实现:

CreateProductA();

CreateProductB();

它们分别返回ProductA3(继承自AbstractProductA)、ProductB3(继承自AbstractProductB)。

也就是说,如果新增了系列3,Client程序可以完全不用改动,可能只需要改一些配置文件,增加一些新dll就可以应对变化。

四、模式的组成

可以看出,在抽象工厂模式的结构图有以下角色:

(1)、抽象产品类角色(AbstractProduct):为抽象工厂中相互依赖的每种产品定义抽象接口对象,也可以这样说,有几种产品,就要声明几个抽象角色,每一个抽象产品角色和一种具体的产品相匹配。
(2)、具体产品类(ConcreteProduct):具体产品类实现了抽象产品类,是针对某个具体产品的实现的类型。
(3)、抽象工厂类角色(Abstract Factory):定义了创建一组相互依赖的产品对象的接口操作,每种操作和每种产品一一对应。
(4)、具体工厂类角色(ConcreteFactory):实现抽象类里面的所有抽象接口操作,可以创建某系列具体的产品,这些具体的产品是“抽象产品类角色”的子类。

五、抽象工厂的具体代码实现

作为长子的我,希望能有一套欧式风格的房子,再加上田园风光,此生足矣。我弟弟就不一样了,他想要一套现代样式的房子,如果兄弟姊妹再多年一点,那就有更多的要求了。由于房子由房顶、地板、窗户和房门组成,其他组件暂时省略,有这么多套房子要建设,每套房子的房顶、地板、窗户和房门都是一个体系的,那就让我们看看如何使用【抽象工厂】模式来实现不同房屋的建造。

//抽象道路
public abstract class Road
{
} //抽象房屋
public abstract class Building
{
} //现代风格道路
public class ModernRoad : Road
{
} //现代风格房屋
public class ModernBuilding : Building
{
} //古典风格道路
public class ClassicRoad : Road
{
} //古典风格房屋
public class ClassicBuilding : Building
{
}
//抽象工厂
public abstract class FacilitiesFactory
{
public abstract Road CreateRoad();
public abstract Building CreateBuilding();
} //现代风格
public class ModernFacilitiesFactory : FacilitiesFactory
{
public override Road CreateRoad()
{
return new ModernRoad();
} public override Building CreateBuilding()
{
return new ModernBuilding();
}
} //古典风格
public class ClassicFacilitiesFactory : FacilitiesFactory
{
public override Road CreateRoad()
{
return new ClassicRoad();
} public override Building CreateBuilding()
{
return new ClassicBuilding();
}
}

客户程序:

可以看出,客户程序依赖的全部是抽象类,在客户程序代码中没有出现过任何具体的实现类。因为在系列需要变化的时候,是不需要改变抽象类的,只是增加一个抽象类的实现而已,又由于客户程序只依赖于抽象,所以系列变化的时候客户程序完全无需变化。

internal class GameManager
{ private FacilitiesFactory facilitiesFactory;
private Road road;
private Building building; public GameManager(FacilitiesFactory facilitiesFactory)
{
this.facilitiesFactory = facilitiesFactory;
} public void BuildGameFacilities()
{
road = facilitiesFactory.CreateRoad();
building = facilitiesFactory.CreateBuilding();
} public void Run()
{
Console.WriteLine(road);
Console.WriteLine(building);
}
}

应用到具体程序(现代风格):

可以看出,风格由Modern改变为Classic的时候,我们封装好的GameManager客户程序没有改变,这就是我们想要的结果。GameManager的逻辑非常复杂,现在它的稳定,能够大大方便我们的工作。

GameManager g = new GameManager(new ModernFacilitiesFactory());
g.BuildGameFacilities();
g.Run();

改造

第一种改造

就是在系列对象不发生系列添加的情况下,使用配置文件来进行例子中场景风格的替换。添加一个App.config文件,在其中加入风格设置的字段。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="factoryName" value="ModernFacilitiesFactory"></add>
</appSettings>
</configuration>

然后,在代码中读取这个配置字段,根据配置字段的值来做实现。首先实现一个构建方法,然后再在客户程序中调用。

public static FacilitiesFactory GetInstance()
{
string factoryName = ConfigurationSettings.AppSettings["factoryName"];
FacilitiesFactory f;
switch (factoryName)
{
case "ModernFacilitiesFactory":
f = new ModernFacilitiesFactory();
break;
case "ClassicFacilitiesFactory":
f = new ClassicFacilitiesFactory();
break;
default:
f = null;
break;
} return f;
}
//客户程序
public static void Main()
{
GameManager g = new GameManager(GetInstance());
g.BuildGameFacilities();
g.Run();
}

第二种改造

其实还有一种需求就是扩展新的系列对象,如果还是不需要对客户程序进行维护,而仅是添加了新的系列对象的类,那将是很舒服的一件事。这样我们就可以通过添加DLL并配合配置文件的使用,就能在不修改源程序代码的情况下,扩展出我们需要的新的系列对象

public staticFacilitiesFactory GetInstance()
{
string factoryName = ConfigurationSettings.AppSettings["factoryName"];
FacilitiesFactory f;
if (factoryName != "")
f = (FacilitiesFactory)Assembly.Load(factoryName).CreateInstance(factoryName);
else
f = null;
return f;
}

这样,我们在扩展时仅需将扩展的DLL放在相应的路径下并配合配置文件即实现了我们的扩展。

六、抽象工厂的实现要点

  1. 如果没有应对“多系列对象创建”的需求变化,则没有必要使用AbstractFactory模式,这时候使用简单的静态工厂完全可以。
  2. “系列对象"指的是这些对象之间有相互依赖、或作用的关系,例如游戏开发场景中“道路”与“房屋”的依赖,“道路”与“地道”的依赖。
  3. AbstractFactory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
  4. AbstractFactory模式经常和FactoryMethod模式共同组合来应对“对象创建”的需求变化。

抽象工厂模式的优点:

【抽象工厂】模式将系列产品的创建工作延迟到具体工厂的子类中,我们声明工厂类变量的时候是使用的抽象类型,同理,我们使用产品类型也是抽象类型,这样做就尽可能的可以减少客户端代码与具体产品类之间的依赖,从而降低了系统的耦合度。耦合度降低了,对于后期的维护和扩展就更有利,这也就是【抽象工厂】模式的优点所在。可能有人会说在Main方法里面(这里的代码就是客户端的使用方)还是会使用具体的工厂类,对的。这个其实我们通过Net的配置,把这部分移出去,最后把依赖关系放到配置文件中。如果有新的需求我们只需要修改配置文件,根本就不需要修改代码了,让客户代码更稳定。依赖关系肯定会存在,我们要做的就是降低依赖,想完全去除很难,也不现实。

抽象工厂模式的缺点:

有优点肯定就有缺点,因为每种模式都有他的使用范围,或者说要解决的问题,不能解决的问题就是缺点了,其实也不能叫缺点了。【抽象工厂】模式很难支持增加新产品的变化,这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。

抽象工厂模式的使用场景:

如果系统需要多套的代码解决方案,并且每套的代码方案中又有很多相互关联的产品类型,并且在系统中我们可以相互替换的使用一套产品的时候可以使用该模式,客户端不需要依赖具体实现。

七、.NET中抽象工厂模式实现

微软的类库发展了这么多年,设计模式在里面有大量的应用,【抽象工厂】模式在.NET类库中也存在着大量的使用,比如和操作数据库有关的类型,这个类就是System.Data.Common.DbProviderFactory,这个类位于System.Data.dll程序集中。该类扮演抽象工厂模式中抽象工厂的角色,DbProviderFactory就是【抽象工厂】模式UML里面AbstractFactory类型。其他具体的工厂类型继承DbProviderFactory类型。

创建型模式(三) 抽象工厂模式(Abstract Factory)的更多相关文章

  1. Java设计模式(3)——创建型模式之抽象工厂模式(Abstract Factory)

    一.概述 抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式.抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象. UML图: 其他的过多概念不再 ...

  2. 创建类模式(二):抽象工厂(Abstract Factory)

    定义 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类. 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态.抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式. ...

  3. [19/04/23-星期二] GOF23_创建型模式(工厂模式、抽象工厂模式)

    一.工厂模式(分为:简单工厂模式.工厂方法模式.抽象工厂模式) 实现了创建者和调用者的分离 核心本质:1.实例化对象,用工厂方法代替new操作:2.将选择实现类.创建对象统一管理和控制,从而将调用者跟 ...

  4. 设计模式的征途—3.抽象工厂(Abstract Factory)模式

    上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...

  5. 设计模式的征途—4.抽象工厂(Abstract Factory)模式

    上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...

  6. 抽象工厂(Abstract Factory)模式

    一.抽象工厂(Abstract Factory)模式 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态. 为了方便引进抽象工厂模式,引进一个新概念:产品族(Product Family ...

  7. java设计模式 -------- 创建模式 之 抽象工厂模式

    本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 工厂方法模式和抽象工厂模式不好区分清楚: 工厂方法模式特点: 1. 一个抽象产品 ...

  8. Java开发中的23中设计模式详解(一)工厂方法模式和抽象工厂模式

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  9. Java设计模式之【工厂模式】(简单工厂模式,工厂方法模式,抽象工厂模式)

    Java设计模式之[工厂模式](简单工厂模式,工厂方法模式,抽象工厂模式) 工厂模式出现的原因 在java中,创建一个对象最简单的方法就是使用new关键字.但在一些复杂的业务逻辑中,创建一个对象不只需 ...

随机推荐

  1. IDEA 2019 注册码

     CATF44LT7C-eyJsaWNlbnNlSWQiOiJDQVRGNDRMVDdDIiwibGljZW5zZWVOYW1lIjoiVmxhZGlzbGF2IEtvdmFsZW5rbyIsImFz ...

  2. LeetCode 1047. 删除字符串中的所有相邻重复项(Remove All Adjacent Duplicates In String)

    1047. 删除字符串中的所有相邻重复项 1047. Remove All Adjacent Duplicates In String 题目描述 LeetCode1047. Remove All Ad ...

  3. LeetCode 187. 重复的DNA序列(Repeated DNA Sequences)

    187. 重复的DNA序列 187. Repeated DNA Sequences 题目描述 All DNA is composed of a series of nucleotides abbrev ...

  4. Prometheus入门到放弃(7)之redis_exporter部署

    redis监控,prometheus需要使用redis_exporter客户端. 这里我们采用docker方式部署,既可以部署在redis所在服务器,也可以部署在其他机器: docker镜像地址:ht ...

  5. Django框架之DRF 基于mixins来封装的视图

    基础视图 示例环境搭建:新建一个Django项目,连接Mysql数据库,配置路由.视图函数.序列化单独创建py文件 # 配置路由 from django.conf.urls import url fr ...

  6. python基础 — Selenium 详细介绍

    一.Selenium+Python环境搭建及配置 1.1 selenium 介绍 selenium 是一个 web 的自动化测试工具,不少学习功能自动化的同学开始首选 selenium ,因为它相比 ...

  7. Java 总结篇2

    第02章:数据类型和运算符 一.概述: 1.数据类型:int.float.char.boolean 2.运算符:算术运算符.赋值运算符.关系运算符.逻辑运算符.位运算符(了解即可).条件运算符 3.基 ...

  8. xorm -Get方法实例

    查询单条数据使用Get方法,在调用Get方法时需要传入一个对应结构体的指针,同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询 package main import ( & ...

  9. 作业调度框架Quartz.NET-现学现用-01-快速入门

    原文:作业调度框架Quartz.NET-现学现用-01-快速入门 前言 你需要应用执行一个任务吗?这个任务每天或每周星期二晚上11:30,或许仅仅每个月的最后一天执行.一个自动执行而无须干预的任务在执 ...

  10. Python之TensorFlow的数据的读取与存储-2

    一.我们都知道Python由于GIL的原因导致多线程并不是真正意义上的多线程.但是TensorFlow在做多线程使用的时候是吧GIL锁放开了的.所以TensorFlow是真正意义上的多线程.这里我们主 ...