定义一个创建对象的接口让子类去决定实例化哪个类。FactoryMethod使一个类的实例化延迟到其子类。

工厂方法即Factory Method,是一种对象创建型模式。

工厂方法的目的是使得创建对象和使用对象是分离的,并且客户端总是引用抽象工厂抽象产品(也就是说使用Product和Factory)

┌─────────────┐       ┌─────────────┐
│ Product │抽象产品│ Factory │抽象工厂
└─────────────┘ └─────────────┘
▲ ▲
│ │
┌─────────────┐ ┌─────────────┐
│ ProductImpl │<─ ─ ─│ FactoryImpl │
└─────────────┘ └─────────────┘


我们以具体的例子来说,假设我们希望实现一个解析字符串到Number的Fctory,可以定义如下:



public interface NumberFactory {
Number parse(String s);
}


有了工厂接口,再编写一个工厂的实现类:



public class NumberFactoryImpl implements NumberFactory {
public Number parse(String s) {
return new BigDecimal(s);
}
}


而产品接口是Number,NumberFactoryImpl返回的产品实际上是BigDecimal。

那么客户端如何创建NumberFactoryImpl呢?通常我们会在接口Factory中定义一个静态方法getFactory来获取真正的子类:

public interface NumberFactory {
// 创建方法:
Number parse(String s);

// 获取工厂实例:
static NumberFactory getFactory() {
return impl;
}

static NumberFactory impl = new NumberFactoryImpl();
}


在客户端中,我们只需要和工厂接口NumberFactory以及抽象产品Number打交道:



NumberFactory factory =NumberFactory.getFactory();

Number result=factory.parse(“123456”);


调用方可以完全忽略真正工厂NumberFactoryImpl和实际产品BigDecimal,这样做的好处是允许创建产品的代码独立的变化,而不会影响到调用方,体现面向对象的思维。

有人会问:一个简单的parse()需要写这么复杂的工厂吗?实际上大多数情况下我们并不需要抽象工厂(这里指的是通过接口的静态方法获取到的impl向上转型的实例),而是通过静态方法职级返回产品,即:



public class NumberFactory {
public static Number parse(String s) {
return new BigDecimal(s);
}
}


 

这种简化的使用静态方法创建产品的方式称为静态工厂方法(Static Factory Method)。静态工厂方法广泛地应用在Java标准库中,例如:



Integer n = Integer.valueOf(100);


Interger又是产品又是静态工厂。它提供静态方法valueOf()来创建Interger。Mamet这种方式和直接写new Interger(100)有什么区别呢?我们观察valueOf()方法:



public final class Integer {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
...
}


它的好处在于,valueOf()内部可能会使用new去新创建一个新的Interger实例,但也可能直接返回一个缓存的Interger实例。对于调用放来说没有必要知道Interger创建的细节。

工厂方法可以隐藏创建产品的细节,而且不一定每次都会真正的创建产品,完全可以返回缓存的产品,从而提升速度并且减少内存消耗。

如果调用放直接使用Interger n=new Interger(100),那么就失去了使用缓存优化的可能性。

我们经常使用的一个静态工厂方法是 List.of():

List<String> list =List.of("A","B","C");

这个静态工厂方法接收可变参数然会返回List接口。需要注意的是,调用方获取的产品总是List接口,而且并不去关心它的实际返回的类型,这与我们给哭护短不能把总是使用的抽象工厂以及抽象产品时一样好的,即使调用方知道List产品的实际类型是java.util.ImmutableCollection$ListN,也不要去强制转型为子类,因为静态工厂方法List.of()保证返回List,但也可以完全可以修改为返回java.util.ArrayList。这里就是里氏替换原则:返回实现接口的任意子类都可以返祖该方法的要求,且不影响调用方。里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象(子类对象完全有基类的功能),程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象,使用基类对象的时候,当前正常情况下是要调用子类特有的方法的,所以使用后基类的时候调用子类的特有的方法·时就会出先方法不存在的情况。

总是引用接口而非实现类,能运行变换子类而不影响调用方,即尽可能的面向对象编程。

和List.of()类似,我们使用MessageDigest时,为了创建某个摘要算法,总是使用静态工厂方法getInstance(String):



MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");


调用方通过产品名称获得产品实例,不但调用简单,而且获得的引用仍然是​​MessageDigest​​这个抽象类。·