package com.javapatterns.factorymethod;
public class badplantexception extends exception
{
public badplantexception(string msg)
{
super(msg);
}
}
代码清单6. 例外类 badplantexception。
工厂方法模式应该在什么情况下使用
既然工厂方法模式与简单工厂模式的区别很是微妙,那么应该在什么情况下使用工厂方法模式,又应该在什么情况下使用简单工厂模式呢?
一般来说,如果你的系统不能事先确定那一个产品类在哪一个时刻被实例化,从而需要将实例化的细节局域化,并封装起来以分割实例化及使用实例的责任时,你就需要考虑使用某一种形式的工厂模式。
在我们的小花果园系统里,我们必须假设水果的种类随时都有可能变化。我们必须能够在引入新的水果品种时,能够很少改动程序,就可以适应变化以后的情况。因此,我们显然需要某一种形式的工厂模式。
如果在发现系统只用一个产品类等级(hierarchy)就可以描述所有已有的产品类,以及可预见的未来可能引进的产品类时,简单工厂模式是很好的解决方案。因为一个单一产品类等级只需要一个单一的实的工厂类。
然而,当发现系统只用一个产品类等级不足以描述所有的产品类,包括以后可能要添加的新的产品类时,就应当考虑采用工厂方法模式。由于工厂方法模式可以容许多个实的工厂类,以每一个工厂类负责每一个产品类等级,因此这种模式可以容纳所有的产品等级。
在我们的小花果园系统里,不只有水果种类的植物,而且有蔬菜种类的植物。换言之,存在不止一个产品类等级。而且产品类等级的数目也随时都有可能变化。因此,简单工厂模式不能满足需要,为解决向题,我们显然需要工厂方法模式。
关于模式的实现
在实现工厂方法模式时,有下面一些值得讨论的地方。
第一丶在图四的类图定义中,可以对抽象工厂(creator) 做一些变通。变通的种类有
抽象工厂(creator) 不是接口而是抽象类。一般而言,抽象类不提供一个缺省的工厂方法。 这样可以有效地解决怎样实例化事先不能预知的类的问题。
抽象工厂(creator) 本身是一个实类,并提供一个缺省的工厂方法。 这样当最初的设计者所预见的实例化不能满足需要时,后来的设计人员就可以用实工厂类的factory() 方法来置换(override))父类中factory()方法。
第二丶在经典的工厂方法模式中,factory()方法是没有参量的。在本文举例时加入了参量,这实际上也是一种变通。
第三丶在给相关的类和方法取名字时,应当注意让别人一看即知你是在使用工厂模式。
com技术架构中的工厂方法模式
在微软(microsoft)所提倡的com(component object model)技术架构中, 工厂方法模式起着关键的作用。
在com架框里,creator接口的角色是由一个叫作iclassfactory的com接口来担任的。而实类concretecreator的角色是由实现iclassfactory接口的类cfactory(见下图)来担任的。一般而言,对象的创立可能要求分配系统资源,要求在不同的对象之间进行协调等等。因为iclassfactory的引进,所有这些在对象的创立过程中出现的细节问题, 都可以封装在一个实现iclassfactory接口的实的工厂类里面。这样一来, 一个com架构的支持系统只需要创立这个工厂类cfactory的实例就可以了。
图6. 微软(microsoft)的com(component object model)技术架构是怎样工作的。
在上面的序列活动(sequence activity)图中,用户端调用com的库函数cocreateinstance。 cocreateinstance在com架框中以cogetclassobject实现。 cocreateinstance会在视窗系统的registry里搜寻所要的部件(在我们的例子中即cemployee)。如果找到了这个部件,就会加载支持此部件的dll。当此dll加载成功后, cogetclassobject就会调用dllgetclassobject。后者使用new操作符将工厂类cfactory实例化。
下面,dllgetclassobject会向工厂类cfactory搜询iclassfactory接口,返还给cocreateinstance。 cocreateinstance接下来利用iclassfactory接口调用createinstance函数。此时,iclassfactory::createinstance调用new操作符来创立所要的部件(cemployee)。此外,它搜询iemployee接口。在拿到接口的指针后, cocreateinstance释放掉工厂类并把接口的指针返还给客户端。
客户端现在就可以利用这个接口调用此部件中的方法了。
ejb技术架构中的工厂方法模式
升阳(sun microsystem)倡导的ejb(enterprise java beans)技术架构是一套为爪哇语言设计的, 用来开发企业规模应用程序的组件模型。我们来举例看一看ejb架构是怎样利用工厂方法模式的。请考察下面的序列活动图。
图7. 在升阳所提倡的ejb技术架构中, 工厂方法模式也起着关键的作用
在上面的图中,用户端创立一个新的 context 对象,以便利用 jndi 伺服器寻找 ejbobject。在得到这个 context 对象后,就可以使用 jndi 名, 比如"employee", 来拿到 ejb 类 employee 的 home 接口。使用 employee 的 home 接口,客户端可以创立 ejb 对象,比如 ejb 类 employee 的实例 emp, 然后调用 employee 的各个方法。
// 取到 jndi naming context
context ctx = new initialcontext ();
// 利用ctx 索取 ejb home 接口
employeehome home = (employeehome)ctx.lookup("employee");
// 利用home 接口创立一个 session bean 对象
// 这里使用的是标准的工厂方法模式
employee emp = home.create (1001, "john", "smith");
// 调用方法
emp.settel ("212-657-7879");
代码清单7. ejb架构中,home接口提供工厂方法以便用户端可以动态地创立ejb类employee的实例。
jms技术架构中的工厂方法模式
jms定义了一套标准的api,让爪哇语言程序能通过支持jms标准的mom(message oriented middleware 面向消息的中间伺服器)来创立和交换消息(message)。我们来举例看一看jms(java messaging service)技术架构是怎样使用工厂方法模式的。
图8. 在jms技术架构中, 工厂方法模式无处不在
在上面的序列图中,用户端创立一个新的 context 对象,以便利用 jndi 伺服器寻找 topic 和 connectionfactory 对象。在得到这个 connectionfactory 对象后, 就可以利用 connection 创立 session 的实例。有了 session 的实例后,就可以利用 session 创立 topicpublisher的实例,并利用session创立消息实例。
properties prop = new properties();
prop.put(context.initial_context_factory,
"com.sun.jndi.fscontext.reffscontextfactory");
prop.put(context.provider_url, "file:c:/temp");
// 取到 jndi context
context ctx = new initialcontext(prop);
// 利用ctx 索取工厂类的实例
topic topic = (topic) ctx.lookup("mytopic");
topicconnectionfactory tcf = (topicconnectionfactory) ctx.lookup("mytcf");
// 利用工厂类创立connection,这是典型的工厂模式
topicconnection tcon = tcf.createtopicconnectoin();
// 利用connection创立session的实例,又是工厂模式
topicsession tsess = tcon.createtopicsession(false,
session.auto_acknowledge);
// 利用session创立producer的实例,又是工厂模式
topicpublisher publisher = tsess.createpublisher(topic);
// 利用session创立消息实例,又是工厂模式
textmesage msg = tsess.createtextmessage("hello from jeff");
//发送消息
publisher.publish(msg);
代码清单8.
jms架构中,工厂模式被用于创立 connection, session, producer 的实例。
问答题
第1题、在这一节和上一节的类图中,我注意到apple类的类图与strawberry类的类图有一点点不同。在apple类的类图左上角有一个夹子样的标识。请问这个标识代表什么意思。
第2题、在这一节的类图4中,我注意到 concreteproduct 类只出现一次,但实现 product 接口的类实际上可以有很多。这是否可以用在联接 product 和 concreteproduct 之间的线旁注上 1,2,... 表示呢? 记得我在uml图中曾见过这种记号。
第3题、请问在本节的小花果园系统的源代码清单4里,broccoli 类实现两个接口,veggieif 和 plantif。只有 plantif 才与工厂模式有关。为什么不把 veggieif 接口合并到 plantif 接口中去?
第4题、请问在工厂方法模式中,产品(product) 何时应是抽象类,何时应是接口?
第5题、请问在工厂方法 (factory())中,为什么要使用 if 语句作过程性判断来决定创立哪一个产品类,而不使用多形性原则 (polymorphsm) 来创立产品类?
问答题答案
第1题、apple类有性质(property),而strawberry类没有性质。
一个类的成员变量叫做属性(attribute)。性质与属性的区别在于性质是带着一套取值丶赋值方法的属性。一个类有了属性,其类图左上角就会有一只夹子。有些人认为,一个爪哇类有了属性才能被称做爪哇豆(java bean)。这只夹子就表示这个类是一只豆。
一个企业爪哇豆,或 ejb (enterprise javabean) 的类图左上角也会有一只夹子,夹子上面有一个e字以示与普通的爪哇豆的不同(请见下图)。
第2题、不能。在图4中联接 product 和 concreteproduct 之间的线有两条,一条表示两者之间的推广关系 (即有向上箭头的),另一条表示两者之间
闽公网安备 35060202000074号