也来谈谈工厂模式
工厂模式其根本目的是要让生产对象的行为多态。不严格的说就是构造方法多态化。
简单回顾一下什么是多态:某父类型的引用指向某对象,调用的方法是该对象所属子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态挂钩。多态使得软件无需重新编译就能拥有不同的行为。
须注意,展现多态性的前提是对象已然存在。否则很难想象多态会是什么样子。由此可知,构造方法是不存在多态性的,因为构造方法的目的就是生产对象,调用之前还未有对象出生。有时我们也需要生产行为的多态性,比如需要生产很多对象而这些对象的具体子类型无需知道,即如何生产的行为是抽象的,这就是所谓的延迟生产。
假设现处于探索阶段,试图解决生产行为多态性的问题。直接使用构造方法固然不行,我们必须遵循“多态之前要有对象”的原则。设想已经有一个类级结构,也就是多态的载体,然后将构造方法封装在其各个子类型中,也许就能成功。这种朦胧的推导也告诉我们需要一个额外的虚构的类级结构。然而这种为解决软件行为本身而非领域问题所生造出的设计往往容易使初学者为其存在意义大感困惑(比如我)。事实上该解决方案就是工厂方法。
所有的设计模式都符合这样一条规律:为了解决某个问题,采取某种方法,达到某种效果。第一点认识问题的本质最为关键。这个问题当然源于软件本身,软件行为本身。讲解模式的文章很多,现在普遍流行打比方的方式,时不时的来个菜园种菜啊,三藏取经啊,这种形式是很好,寓教于乐,至少吸引住读者眼球,为推播思想起到了不错的糖衣作用。但是我们要注意有没有将模式本质传达给读者。过于噱头的糖衣容易分散注意力。设计模式的本质不是用来解决问题领域的和种菜无关和生产蔬菜是不是多态的无关。
学习设计模式是一个悟道的过程,关键在于领悟而不是单靠记忆。推而广之,以思维模式为例,记住某种思维模式的描述不等于能够同化为自己的思想。这确实是非常形而上学的东西。Gof的书都是从这样一个高度来讲学的,因为它默认你已经接触了足够多的需要模式来解决的问题。所以书中多为总结归纳性的话语,高手读来会心一笑,新手遇到莫名其妙。我以为初学时形而下学更为可取。通过学习(最好能够记住)几个精巧的,能说明问题的,信息完备的,具体的例子,然后在实际运用中去发现模式,运用模式,逐步领会问题的本质,最后再看看形而上学的Gof你也能嫣然一笑。
举个工厂例子。Color基类有子类Red,Green,Blue若干。另有一个神秘的类Mystery,它维护若干Color对象。Mystery依赖于如何生产Color。假设Mystery不可修改,无法重新编译。为了使Mystery行为多变,维护不同的Color,需要用到工厂。
class Red extends Color...
class Green extends Color...
class Blue extends Color...
abstract class ColorFactory{ abstract Color create(); }
class RedFactory extends ColorFactory{ Color create(){ return new Red(); } }
...
class Mystery
{
List<Color> list;
protected void g(Color c)
{
//do something special
}
public void f(ColorFactory factory)
{
Color c = factory.create();
g( c );
list.add( c );
}
public void doSomething()
{
//对list中的每一个Color进行操作
}
}
如上,通过方法f() Mystery维护若干Color对象,Mystery依赖于如何创建Color,但创建行为不是具体的,回顾本文开头。无需关注代码的真实用途,这里想说明的是工厂方法如何使得创建对象的行为变得多态。记住这样一个例子胜过千言万语的理论和概念,然后在运用中类比类推,这样才能领悟其实难以言传的模式本身。
Mystery使用范型也可达到类似效果,但缺乏动态绑定的能力。
上例中Color工厂是虚构的,有时工厂有实际意义。比如:
{
public Object current();
public Boolean hasMore();
public void next();
}
abstract class List
{
abstract public Iterator iterator();
}
class ArrayList extends List
{
private class ArrayListIterator extends Iterator{...}
Iterator iterator()
{
return new ArrayListIterator();
}
}
List其实是一个工厂,但他本身是有具体意义的。这里也说明了工厂的另一个用途:暴露接口。