Abstract Factory(抽象工厂)模式

    有时候,我们希望为客户代码提供实例化类的同时能够控制应该对哪个类进行实例化。在这些情况下,我们可以应用Factory Method模式,并提供一个方法来利用某种外部因素确定应该实例化哪个类。有时候,这些用于控制对哪个类进行实例化的外部因素往往涉及多个类,其本身就是一个研究主题。

 

    Abstract Factory模式的意图在于创建一系列相关或相互依赖的对象

 

1. 经典范例:GUI工具包

  GUI工具包是Abstract Factory模式的最经典例子。一个GUI工具包对象就是一个抽象工厂,提供可以搭建客户界面的GUI控件。每个GUI工具包对象可决定按钮、文本字段和其他控件出现的方式。工具包建立明确的“外观和感觉”,包括背景色、图形、以及其他超出工具包所提供控件系列的GUI设计。我们也许会为整个系统建立统一的“外观和感觉”,也许在不同版本中使用不同的“外观和感觉”,以便于区分版本变更、公司标准图形的变更,或者简单的改进。Abstract Factory模式能够实现“外观和感觉”,使得应用程序更加容易学习和使用。如下图所示的UI类就是类似的例子。

 

UI和BetaUI类的实例是从UI控件族创建的抽象工厂

app.abstractFactory目录下的ShowVisualization应用程序从Visualization类在构造器中接收的UI对象中获取其按钮。其图形化界面允许用户在工厂地析上添加和拖放机器。


Visualization类在面板上的左上解添加机器,允许用户使用鼠标拖放机器的位置。用户可以取消拖放或者添加操作 

下图说明了Visualization类:


                                          
                                                       Visualization类借助UI工厂对象构造GUI 

Visualization类使用UI对象来构造GUI。比如,undoButton()方法的代码如下所示:

protected JButton undoButton() 
{ 
if(undoButton == null) 
{ 
undoButton = ui.createButtonCancel();
 undoButton.setText("Undo");
 undoButton.setEnabled(false); 
undoButton.addActionListener(mediator.undoAction); 
} 
return undoButton; 
}

 

  为获取图形化程序的不同“外观和感觉”,我们可以创建一个重写UI工厂类中某些元素的子类。我们可以把这个GUI工厂的实例传递给Visualization类的构造器。

  假设我们发布具备一些新功能的Visualization类,但同时这些代码处于beta测试阶段,用户界面部分可能需要部分调整。举例说,我们希望使用斜体字,并使用cherry-large.gif和cherry-large-down.gif替换掉火箭图形。BetaUI类的代码应该如下所示:

public class BetaUI extends UI 
{ 
public BetaUI() 
{ 
Font oldFont = getFont(); 
font = new Font(oldFont.getName(), oldFont.getStyle() | Font.ITALIC, oldFont.getSize()); 
} 
public JButton createButtonOk() 
{ 
JButton b = super.createButtonOk();
 b.setIcon(getIcon("images/cherry-large.gif"));
 return b; 
}
 public JButton createButtonCancel() 
{ JButton b = super.createButtonCancel();
 b.setIcon(getIcon("images/cherry-large-down.gif")); 
return b; 
} 
}

 

这部分代码采用的策略是尽可能多的使用基础类的方法。

 

运行如下代码,获得新的界面效果:

package app.abstractFactory 
{ 
//... 
public class ShowBetaVisualization 
{ JPanel panel = new Visualization(new BetaUI()); SwingFacade.launch(panel,"Operational Model"); 
} 
}

  

在无需改变Visualization类代码的前提下,借助于BetaUI就可以改变应用程序的界面效果

 

  程序运行之后会获取如图所示的效果。UI和BetaUI类的实例提供不同的GUI控件族,以实现不同的GUI应用程序界面效果。尽管这是Abstract Factory模式的一个非常有价值的应用,但其设计看起来并不健壮。尤其值得注意的是,BetaUI类依赖于这种能力来重写构造过程,以及访问特定受保护的实例变量---尤其是UI类的font变量。

 

突破题:请对上述设计进行修改,仍旧允许开发GUI控件工厂的变种,但是减少子类对UI类的方法修饰符的依赖。

答:提供更灵活设计思路的方案是在接口中指定期望的创建过程以及标准的GUI属性,如下图所示:

GUI控件的抽象工厂设计可以减少子类对UI类中方法修饰符的依赖

 

 当客户需要新对象时,通过使用Abstract Factory模式,客户便无须知道需要实例化哪个类。从这个角度来说,Abstract Factory模式类似于Factory Method模式的集合。在有些情况下,Factory Method模式的设计也许会逐渐演变为Abstract Factory模式的设计。

 

2.抽象工厂和工厂方法

  工厂方法模式介绍了实现CreditCheck接口的两个。当客户调用CreditCheckFactory类的CreatCreditCheck()方法时,CreditCheckFactory类会实例化其中一个类。具体实例化哪个类,取决于赊购代理是否在线。这种设计思路使得其他开发者无需考虑赊购代理的状态。下图了CreditCheckFactory类和CreditCheck接口的当前实现。

 Factory Method模式使得客户可以在进行赊购审查时无须知道需要实例化哪个类

 CreditCheckFactory类提供关于顾客信用卡的赊购代理信息。除此之外,credit包包含查看顾客购买和支付信息的类。下图当前的com.oozinoz.credit包。

本包中的类检查顾客的信用卡、购买地址和支付地址


  现在假设需求分析师告诉你Oozinoz公司希望能够为加拿大的顾客服务。为了把业务扩展到加拿大,你需要使用不同的赊购代理和不同的数据源来获取购买和支付信息。

  当客户给oozinoz公司打电话的时候,呼叫中心应用程序需要一些对象来执行各种检查。具体使用哪些对象取决于呼叫是来自美国还是加拿大。你可以使用Abstract Factory模式来创建这些对象。

  把业务扩展到加拿大后,支持应用程序进行信用检查所需要的类几乎是以前的一倍。假设你使用三个包来维护这些类。现在,credit包中包含三个“检查”接口和一个抽象工厂类。 该抽象工厂类有三个create()方法分别负责创建完成信用、购买和支付等信息的对象。还可以把CreditCheckOffline类放入该包中,这样无论客户是从哪里打来的电话,我们都可以使用该类进行离线验证。下图给出了修改后com.oozinoz.credit包的完整组成结构。

 


经修改后,该类包含主要接口和一个抽象工厂类

  为了使用具体类来实现credit中的接口,可以引入两个新包:com.oozinoz.credit.us和com.oozinoz.credit.ca。每个包都包含了抽象工厂类的具体类,以及实现credit中每个接口的若干类。

 

突破题:下图描述了com.oozinoz.credit.ca包中类及其与credit包中类和接口的关系,请将该图补充完整。 


com.oozinoz.credit.ca包提供一套具体类,用于对加拿大打来的赊购电话进行各种审查

另外,只需从CreditCheckOffline抽象类派生出一个具体的类,因为oozinoz公司离线审查类既可用于处理美国打来的赊购电话,也可用于处理加拿大打来的赊购电话。

 

分别为加拿大客户和美国客户服务的两个具体工厂类都比较简单;它们的返回类型分别是用于加拿大客户的check接口和美国用户的check接口;不过,如果当地赊购代理离线,那么这两个工厂类都将返回CreditCheckOffline对象。CreditCheckFactory类仍可使用Factory Method模式中提到的isAgencyUp()方法来判断赊购代理是否在线。

 

自我突破题:请完成CheckFactoryCanada.java文件中如下所示的代码:

package com.oozinoz.credit.ca;
 import com.oozinoz.credit.*;
 public class CheckFactoryCanada extends CreditCheckFactory 
{ public BillingCheck createBillingCheck() 
{ 
return new BillingCheckCanada(); 
} 
public CreditCheck createCreditCheck() 
{ 
if(isAgencyUp()) return new CreditCheckCanadaOnline();
 return new CreditCheckOffline(); //若代理不在线,则直接返回一个CheckOffline()对象 
} 
public ShippingCheck createShippingCheck() {
 return new ShippingCheckCanada(); 
} 
} 

 

 

一个正确的答案应该包括以下三点:

(1)实现从CreditCheckFactory类继承的create-方法。

(2)拥有每个create-方法返回的合适接口。

(3)如果赊购代理离线,应该返回CreditCheckOffline对象。

 

到现在为止,我们已经通过Abstract Factory模式完成了该设计。该设计可以创建两个不同系列的对象;每个系列的各个对象分别负责验证客户某一方面的信息。这两个对象系列中,一个系列用于美国客户信息验证,另一个系列用于加拿大客户信息验证。抽象类creditCheckFactory的实例将是CheckFactoryCanada类或者是CheckFactoryUS类。这些对象是抽象工厂,能够创建验证不同国家顾客的信用、购买和支付信息的对象。

 

 

3.包和抽象工厂

  从广义上来说,包通常是类的系列;而抽象工厂可以创造对象的系列。在前面所述的例子中,我们将用于加拿大客户的抽象工厂和用于美国客户的抽象工厂分别放在不同的包中,并将这些工厂类产生对象所需的公共接口放在另一个包中。

 

突破题:我们将每个工厂类及相关类放入独立的包中,你认为这样做合理吗?为什么,如果认为不合理,请给出一种你认为合理的做法。

答:赞成方观点:将与每个国家相关的类分别放在不同的包中,这将有利于oozinoz公司的开发人员组织他们的软件和开发工作。因为按照这种方式组织的包相互独立;这样,我们就可以很容易地实现对其他国家的支持。

     反对方观点:这种方式成本太高。有人更愿意将所有类放在同一包中,如果不需要为九个或者更多的国家提供服务项目,那么他们是不会考虑扩展的。在具体的开发环境中,这样做也便于开发人员浏览和对比同一类型的所有类。

 

4. 小结

    Abstract Factory模式可以为客户创建相互关联或相互依赖的对象系列的部分对象。这个模式的最经典范例是“外观和感觉”系列--GUI控件族。当然有的主题超出了这个对象族,诸如顾客的国家。与Factory Method模式一样,Abstract Factory模式使得客户无须知道实例化哪个类。借助于Factory Method模式,可以为客户提供工厂类,每个工厂类都可用于创建一系列与某一公共主题相关的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值