简单的说,简单工厂模式就是由一个工厂来决定创建出那一种产品类的的实例。这类产品必须都是有相同的父类或者接口。
所以列,它通常涉及到三个角色:
- 工厂类:也是这个模式的核心,包含了所有的与对象创建相关的所有商业逻辑。
- 抽象采品:通常是接口或者抽象类,是工厂类的返回值类型,是工厂类创建出的产品的父类或接口
- 具体产品:继承或者实现抽象产品的具体产品。
他的特征除了以上三个角色以外,还有一个:工厂方法为static的,至于这点我们到后面来说。
典型的代码如下:
public class Factory{
public static Product create(){
//具体的创建逻
return new ConcreteProduct();
}
}
public interface Product{}
public class ConcreteProduct implements Product{
}
接下来我们就来找找这个模式在Java API中的应用。书中提到了这么一个类:DateFormat.的三个getTimeInstance()方法,那么我们就进去看看源码。
/**
* Gets the time formatter with the default formatting style
* for the default locale.
* @return a time formatter.
*/
public final static DateFormat getTimeInstance()
{
return get(DEFAULT, 0, 1, Locale.getDefault());
}
/**
* Gets the time formatter with the given formatting style
* for the default locale.
* @param style the given formatting style. For example,
* SHORT for "h:mm a" in the US locale.
* @return a time formatter.
*/
public final static DateFormat getTimeInstance(int style)
{
return get(style, 0, 1, Locale.getDefault());
}
/**
* Gets the time formatter with the given formatting style
* for the given locale.
* @param style the given formatting style. For example,
* SHORT for "h:mm a" in the US locale.
* @param aLocale the given locale.
* @return a time formatter.
*/
public final static DateFormat getTimeInstance(int style,
Locale aLocale)
{
return get(style, 0, 1, aLocale);
}
可以很明显的看出,这个三个方法都是调用了get()方法,那我们继续跟踪看一下具体实现:
private static DateFormat get(int timeStyle, int dateStyle,
int flags, Locale loc) {
if ((flags & 1) != 0) {
if (timeStyle < 0 || timeStyle > 3) {
throw new IllegalArgumentException("Illegal time style " + timeStyle);
}
} else {
timeStyle = -1;
}
if ((flags & 2) != 0) {
if (dateStyle < 0 || dateStyle > 3) {
throw new IllegalArgumentException("Illegal date style " + dateStyle);
}
} else {
dateStyle = -1;
}
try {
// Check whether a provider can provide an implementation that's closer
// to the requested locale than what the Java runtime itself can provide.
LocaleServiceProviderPool pool =
LocaleServiceProviderPool.getPool(DateFormatProvider.class);
if (pool.hasProviders()) {
DateFormat providersInstance = pool.getLocalizedObject(
DateFormatGetter.INSTANCE,
loc,
timeStyle,
dateStyle,
flags);
if (providersInstance != null) {
return providersInstance;
}
}
return new SimpleDateFormat(timeStyle, dateStyle, loc);
} catch (MissingResourceException e) {
return new SimpleDateFormat("M/d/yy h:mm a");
}
}
我们看看重点部分:
首先创建了一个LocaleServiceProviderPool,然后将获取实例的具体逻委托给pool,如果获取不到,则返回一个SimpleDateFormat的实例。
其实这个方法他就是一个很明显的简单工厂方法,只不过是这个抽象类自己同时充当了工厂类的角色。
为什么工厂方法是static? 就以DateFormat为例,首先他是一个抽象类,不能自己实例化。如果工厂方法是不静态方法,那么将无法调用,那么我们假设他不是抽象类,那么我们再试想一下:
首先我们创建了一个DateFormat实例,然后调用他的getTimeInstance()方法,来获取一个他的子类的实例。你会不会觉得很别扭,既然我已经有了如下语句:
DateFormat format = new DateFormat(); 为什么我不直接就写DateFormat format = new SimpleDateFormat()列? 还要通过format.getTimeInstance()多此一举列?
其实这个简单工厂方法对于开--闭原则的支持有限。如果要扩展,必须修改工厂方法。有一点要申明的是:简单工厂模式并不是单例模式,但它是单例模式的基础。与单例模式有着很大程序的区别。在后面的文件中,我们将会陆续介绍到。那么我们来看一个实际的例子(从书上Copy过来):
农场有很多水果:苹果、葡萄、草莓,假设现在有一名园丁看守农场,我们需要什么,直接问园丁要即可,园丁会帮我们会采摘过来。那么分析一下: 在这个案例中: 水果就是抽象采品,而苹果、葡萄则是具体产品,园丁则是扮演工厂角色。那么则会有如下代码:
public interface Fruit { void grow(); void plant(); void harvest(); } package com.pattern.factory.simple; public class Apple implements Fruit{ private int treeAge; @Override public void grow() { log("Apple is growiing...."); } @Override public void harvest() { log("Apple has beean harvested...."); } @Override public void plant() { log("Apple has beean planted...."); } public static void log(String msg){ System.out.println(msg); } public int getTreeAge() { return treeAge; } public void setTreeAge(int treeAge) { this.treeAge = treeAge; } } package com.pattern.factory.simple; public class Grape implements Fruit { private boolean seedless; public boolean isSeedless() { return seedless; } public void setSeedless(boolean seedless) { this.seedless = seedless; } public static void log(String msg){ System.out.println(msg); } @Override public void grow() { log("Grape is growing..."); } @Override public void harvest() { log("Grape has been harvested....."); } @Override public void plant() { log("Grape has been planted........"); } } package com.pattern.factory.simple; public class Strawberry implements Fruit { public static void log(String msg){ System.out.println(msg); } @Override public void grow() { // TODO Auto-generated method stub log("Strawberry is growing...."); } @Override public void harvest() { log("Strawberry has been harvested.........."); } @Override public void plant() { log("Strawberry has been planted..........."); } } package com.pattern.factory.simple; public class FruitGardener { public static Fruit factory(String which) throws BadFruitException{ if(which.equalsIgnoreCase("apple")){ return new Apple(); } if(which.equalsIgnoreCase("strawberry")){ return new Strawberry(); } if(which.equalsIgnoreCase("grape")){ return new Grape(); } throw new BadFruitException("bad fruit Exception "); } }
现在我们开始问园丁要苹果:package com.pattern.factory.simple; public class TestSimple { /** * @param args * @throws BadFruitException */ public static void main(String[] args) throws BadFruitException { Fruit fruit = FruitGardener.factory("apple"); fruit.grow(); } }
从打印结果很明显可以看出,fruit的实例类型是Apple。那么写完这个例子,我们有一点值得反思: 一个农场不可能只有三种水果,那么现在我们种植了更多的水果,如:香蕉、梨、西瓜..... 那么现在我们肯定首先是扩展具体产品,接着是要修改园丁类的工厂方法。 那么显然违背了开---闭原则中的“闭”。而此时,我们的园丁是集万千任务于一身,假设园丁因为某些原因不能工作了,那么怎么找谁要水果去列? 所以这里也体现了简单工厂模式的一个问题: 扩展时,需要修改已有的代码,且工厂类的职能过于集中,若工厂类出现问题,则整个模块将不能正常工作。那么有没有改进办法列? 还是那句话:你永远不会是第一个发现问题的。那么接下来要介绍的工厂方法模式,就针对这个问题,就行了改进了。