工厂模式属于类的创建模式,工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,而不必事先知道每次要实例化哪一个类。工厂模式有三个基本形态:
1. 简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method
Pattern)。
2. 工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式。
3. 抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。
我们用图例来理解简单工厂模式的基本语义。(以下所有图示只是理解的代表形式,为了表达清楚,在某些方面可能违背建模图示法中的基本语义)。
图1
简单工厂模式的掌握,对学习单例模式和多例模式起着非常重要的作用。
图1表示农场的水果种植情况,很明显,我们可知:Fruit接口类有三个方法(plant,harvest,grow);由于Apple和Grape类实现了Fruit接口,所以必须实现Fruit类中所有的方法,其中Apple 和Grape各自有相关的方法。至于他们之间的关系,代码我就不列出。
农场的园丁(
FruitGardener )也是系统的一部分,自然要由一个合适的类来代表。这个类就是FruitGardener 类,
FruitGardener 类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple)、葡萄(Grape)的实例。而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException 异常。
/*********************以下为农场的园丁类
*********************/
public class FruitGardener
{
/**
* 静态工厂方法
*/
public static Fruit factory(String which) throws BadFruitException
{
if (which.equalsIgnoreCase("apple"))
{
return new Apple();
}
else if (which.equalsIgnoreCase("grape"))
{
return new Grape();
}
else
{
throw new BadFruitException("Bad fruit request");
}
}
}
/*********************以下为自定义异常类*********************/
public class BadFruitException extends Exception
{
public BadFruitException(String msg)
{
super(msg);
}
}
/*********************用户调用*********************/
………………
try
{
FruitGardener.factory("grape");
FruitGardener.factory("apple");
FruitGardener.factory("xxx");
}
catch(BadFruitException e)
{
...
}
…………………
|
由此可见, 简单工厂涉及到三个角色:
1. 工厂类角色:负责创建产品的对象。如上面的FruitGardener类。它是工厂方法模式的核心。它往往由一个具体的类实现。
2. 抽象产品角色:实现了产品的共同接品,它可以由抽象类或接口实现。如上面的Fruit
3. 具体的产品:某一具体的产品,如上面的Apple和Grape
注意
1. 厂方法的中的工厂角色也可以由抽象产品角色扮演。即抽象产品角色担任了对象的创建。在java API中如:java.text.DateFormat 此时,
抽象产品角色被定义成一个抽象的类,而不是一个接口。(抽象类与接口之间的区别与应用,在后面我会继续列出相关内容)。一个抽象类不能有自己的实例, DateFormat 的工厂方法是静态方法,并不是普通的方法。也就是说,它直属于类本身,不通过类的实例化。
图2
2. 如果抽象产品角色被省略,则工厂角色可以与具体产品角色合并。
public class FruitGardener
{
public FruitGardener (){}
/**
* 静态工厂方法
*/
public static FruitGardener factory()
{
return new FruitGardener ();
}
}
|
以上两点类似单例模式和多类模式,但不等于单例或多例模式。在后面模式设计中,会提到它,先别急。这是简单工厂模式的一种退化现象。如下代码:
简单工厂模式与其它模式的关系
1.
与单例模式的关系
单例模式使用了简单工厂模式。换言之,单例类具有一个静态工厂方法提供自身的实例。一个抽象产品类同时是子类的工厂。(如图2所示)
但是单例模式并不是简单工厂模式的退化情形,
单例模式要求单例类的构造方法是私有的,从而客户端不能直接将之实例化,而必须通过这个静态工厂方法将之实例化,而且单例类自身是自己的工厂角色。换言之,单例类自己负责创建自身的实例。单例类使用一个静态的属性存储自己的惟一实例 ,工厂方法永远仅提供这一个实例。(说到这里,我简单有点兴奋,终于知道 Sun提供的javaAPI中为什么会采用这些模式 )
2.
与多例模式的关系
多例模式是对单例模式的推广。多例模式与单例模式的共同之处在于
它们都禁止外界直接将之实例化,同时通过静态工厂方法向外界提供循环使用的自身的实例。它们的不同在于单例模式仅有一个实例,而多例模式则可以有多个实例。
多例模式往往具有一个聚集属性,通过向这个聚集属性登记已经创建过的实例达到循环使用实例的目的。一般而言,一个典型的多例类具有某种内部状态,这个内部状态可以用来区分各个实例;而对应于每一个内部状态,都只有一个实例存在。
注 :聚集表示类之间的关系是整体与部分的关系,它分:共享聚集和组成;
共享聚集中的部分可以是多个整体的一部分;而组成 仅 表示整体与部分关系,整体消失,则部分也消失。需要注意的是 ,一些面向对象大师对聚集的定义并不一样。
其它理解多例模式,并不困难,我们可以通过javaAPI来理解,尤其是聚集关系。
根据外界传入的参量,工厂方法可以查询自己的登记聚集,如果具有这个状态的实例已经存在,就直接将这个实例提供给外界;反之,就首先创建一个新的满足要求的实例,将之登记到聚集中,然后再提供给客户端。
关于单例模式和多例模式详细的细节,在后面会描述。
3.
备忘录模式
单例和多例模式使用了一个属性或者聚集属性来登记所创建的产品对象, 以便可以通过查询这个属性或者聚集属性找到和共享已经创建了的产品对象。这就是备忘录模式的应用。
4. MVC
模式
MVC
模式并不是严格意义上的设计模式,而是在更高层次上的架构模式。
MVC
模式可以分解成为几个设计模式的组合,包括合成模式、策略模式、观察者模式,也有可能会包括装饰模式、调停者模式、迭代子模式以及工厂方法模式等。
简单工厂模式所创建的对象往往属于一个产品等级结构,这个等级结构可以是MVC模式中的视图(View);而工厂角色本身可以是控制器(Controller)。一个MVC 模式可以有一个控制器和多个视图,如下图所示。
图3
我们结合简单工厂方法,可知上图中的Controller(控制器)也就是工厂角色,(FruitGardener)它负责创建产品View(视图)。如果系统需要有多个控制器参与这个过程的话,简单工厂模式就不适用了,应当考虑使用工厂方法模式,我们以后描述它。
简单工厂方法的应用
使用静态工厂方法是为了将具体子类实例化的工作隐藏起来,从而客户端不必考虑如何将具体子类实例化,因为抽象类
DateFormat 会提供它的合适的具体子类的实例。这是一个简单工厂方法模式的绝佳应用。
利用具体产品类的超类类型将它的真实类型隐藏起来,其好处是提供了系统的可扩展性。如果将来有新的具体子类被加入到系统中来,那么工厂类可以将交给客户端的对象换成新的子类的实例,而对客户端没有任何影响。
这种将工厂方法的返还类型设置成抽象产品类型的做法,叫做针对抽象编程,这是依赖倒转原则(
DIP)的应用。我们后面会描述“依赖倒转原则(DIP)”的具体应用。
该模式的缺点
大家不难看出,如果新增加产品如:orange,则需要修改工厂类(FruitGardener),从而限制了其灵活性;而对于产品角色类适合,因此简单工厂只在有限的程度上支持“开–闭”原则。
工厂类集成了所有的创建工作,显得工厂类负任比较得,万一出问题,所有的创建工作将不能进行。
当产品类有不同的接口种类时,工厂类需要判断在什么时候创建某种产品。这种对时机的判断和对哪一种具体产品的判断逻辑混合在一起,使得系统在将来进行功能扩展时较为困难。这一缺点在工厂方法模式中得到克服。
由于简单工厂模式使用静态方法作为工厂方法,而静态方法无法由子类继承,因此,
工厂角色无法形成基于继承的等级结构。这一缺点会在工厂方法模式中得到克服。
好了,关于工厂模式中的简单工厂模式就描述到这,下一节,主要描述工厂模式中的工厂方法。