天天看点

抽象工厂模式

一、 抽象工厂模式(Abstract Factory)模式 

  抽象工厂模式(Abstract Factory)定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体的类.

  抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向Client端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据LSP原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。

二、 抽象工厂模式的角色与结构:

  抽象工厂(Abstract

Factory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统商业逻辑无关的。

  具体工厂(Concrete

Factory)角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。

  抽象产品(Abstract

Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。

  具体产品(Concrete Product)角色:这个角色用以代表具体的产品。

  抽象工厂模式就相当于创建实例对象的new,由于经常要根据类生成实例对象,抽象工厂模式也是用来创建实例对象的,所以在需要新的事例对象时便可以考虑是否使用工厂模式。虽然这样做可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

  结构图:

      

基本代码: 

抽象工厂模式
抽象工厂模式

View Code

三、抽象工厂模式与工厂方法模式的区别

  工厂模式:定义一个用于创建对象的借口,让子类决定实例化哪一个类

  抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类

  它们区别在于:如果产品单一的时候,最合适是工厂模式,但如果有多个业务品种,业务分类时,通过抽象工厂模式产生需要的对象

是一种非常好的解决方式. 工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。

  看一下工厂方法与抽象工厂的对比:    

工厂方法模式

抽象工厂模式

针对的是一个产品等级结构

针对的是面向多个产品等级结构

一个抽象产品类

多个抽象产品类

可以派生出多个具体产品类

每个抽象产品类可以派生出多个具体产品类

一个抽象工厂类,可以派生出多个具体工厂类

每个具体工厂类只能创建一个具体产品类的实例

每个具体工厂类可以创建多个具体产品类的实例

四、抽象工厂模式的实例

  下面用个实例来说明一下抽象工厂模式。

  项目需要换DataBase,估计很多人也遇到过,但由于项目设计考虑不足,而导致更换database后,引起一系列的程序操作数据库问题,确实很让人头疼。而且网上也有很多人把这个更换database的作为抽象工厂模式的实例。刚好不巧,偶也中过枪。在之前做的项目中,有一个项目原来是用SQLServer数据库的,但后来客户说需要更换成Oracle数据库。好吧,那我们就修改吧,客户是老大。在整个修改过程中,工作量倒不是很大,主要是修改一些Oracle与SqlServer之间语法的转换(PL/SQL与T_SQL的区别挺大),修改工作还是比较顺利,当时自己也没有怎么在意这个工作过程。后来,当我自己去学习设计模式的时候,才发现如果当初那项目的设计比较糟糕的话,那么更换DB所带的工作是相当大的。呵呵,还好当初设计那项目的前辈考虑比较周全,也方便了我们后来的修改,维护工作。

     假设数据库有Users与Department两个表需要查询以及插入一条纪录,用抽象工厂实现如下:

抽象工厂模式
抽象工厂模式

这样对于客户端调用来说,不是需要知道是SqlServer还是Oracle,只需要通过抽象操作实例,产口具体类名也被具体工厂的实现分离,不会出现在客户代码中,符合开放-封闭

的原则。

不过,这种设计也是有缺点的,比如需要添加一个新的表Project的时候,那么就需要添加三个类,IProject,SqlServerProject,OracleProject,而且还需要修改IFactory,SqlserverFactory,OracleFactory才能实现,这样修改动作也是比较大的。如果项目有许多调用数据库的类,那么要修改的地方就相当的多了。

那样需要对此设计再作优化,通过用简单工厂来优化抽象工厂,去除IFactory,SqlserverFactory,OracleFactory三个工厂类,然后用一个DataAccess类实现,用一个简单工厂模式去实现。

代码如下:

抽象工厂模式
抽象工厂模式

 这样一来,DataAccess类代替了IFactory,SqlserverFactory,OracleFactory三个工厂类,因为在DataAccess类中已经设置了db的值,所以简单工厂就不再需要输入参数,

客户端只需要通过DataAccess来创建数据库访问实例类,无必出现任何一个Sqlserver或者Oracle的字样,从而达到解耦的目的。

 因为DataAccess中存在switch-case来判断当前db是Sqlserver还是Oracle中,当如果项目变动,需要把数据库修改成db2

或者access时,这样DataAccess类中的每个switch-case方法都修改,添加对应的数据库。

这样还是比较麻烦的。不过,在switch-case中,是通过字符串去实例化对应的数据库,如果能通过字符串找到应该实例化的类是那

一个,就可以去除switch了。这样的话,通过反射的方式对于DataAccess作进一步优化。

抽象工厂模式
抽象工厂模式

 还有,因为DataAccess中的db字符串是写死,如果写在配置文件里面,那样程序会更加灵活,DataAccess类就不用因为数据库变动而需要修改。

五、抽象工厂模式的优缺点 

1) 它分离了具体的类    Abstract

Factory模式帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。

2) 它使得易于交换产品系列   

一个具体工厂类在一个应用中仅出现一次—即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变

3) 它有利于产品的一致性   

当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而A b s t r a c t F a c t o r

y很容易实现这一点。

4) 难以支持新种类的产品    难以扩展抽象工厂以生产新种类的产品。这是因为A b s

t r a c t F a c t o r y接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及A b s t r a c t F

a c t o r y类及其所有子类的改变。 (可以通过简单工厂改造,实现支持新种类的产品)。