参考源
工厂模式(Factory Pattern)属于创建型模式
普通工厂模式
在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
如果需要创建一个对象,最简单的方式就是直接 new 一个。
而工厂模式代替了传统的直接 new 的形式,那么为什么要替代呢?
如果所有的对象都通过 new 的方式去创建,那么当程序中大量使用此对象时,突然有一天这个对象的构造方法或是类名发生了修改,那就得逐个去进行修改。
根据迪米特法则,应该尽可能地少与其他类进行交互,所以可以将那些需要频繁出现的对象创建,封装到一个工厂类中。
当需要对象时,直接调用工厂类中的工厂方法来生成对象,这样,就算类出现了变动,只需要修改工厂中的代码即可,而不是大面积地进行修改。
同时,可能某些对象的创建并不只是一个 new 就可以搞定,可能还需要更多的步骤来准备构造方法需要的参数。
来看看如何使用工厂模式创建对象,既然是工厂,那么就来创建点工厂需要生产的东西:
/*** 水果类*/public class Fruit {private final String name;public Fruit(String name){this.name = name;}}
/*** 苹果类*/public class Apple extends Fruit{public Apple() {super("苹果");}}
/*** 橘子类*/public class Orange extends Fruit{public Orange() {super("橘子");}}
通常情况下,直接 new 就可以得到对象了:
Apple apple = new Apple();Orange orange = new Orange();
现在将对象的创建封装到工厂中:
/*** 水果工厂*/public class FruitFactory {/*** 这里就直接来一个静态方法根据指定类型进行创建* @param type 水果类型* @return 对应的水果对象*/public static Fruit getFruit(String type) {switch (type) {case "苹果":return new Apple();case "橘子":return new Orange();default:return null;}}}
现在就可以使用此工厂来创建对象了:
// 直接问工厂要,而不是自己去创建
Apple apple = (Apple) FruitFactory.getFruit("苹果");
Orange orange = (Orange) FruitFactory.getFruit("橘子");
不过这样还是有一些问题,前面提到了开闭原则,一个软件实体,比如类、模块和函数应该对扩展开放,对修改关闭。
此时如果需要新增一种水果,比如桃子,那么就得去修改工厂提供的工厂方法了,这样是不太符合开闭原则的。
因为工厂实际上是针对于调用方提供的,所以应该尽可能对修改关闭。
工厂方法模式
所以,我们就利用对扩展开放,对修改关闭的性质,将简单工厂模式改进为工厂方法模式,既然不让改,那么就看看如何去使用扩展的形式:
/**
* 水果工厂
* <p>将水果工厂抽象为抽象类,添加泛型 T 由子类指定水果类型
*/
public abstract class FruitFactory<T extends Fruit> {
/**
* 不同的水果工厂,通过此方法生产不同的水果
* @return 水果对象
*/
public abstract T getFruit();
}
/**
* 苹果工厂
*/
public class AppleFactory extends FruitFactory<Apple>{
/**
* 获取苹果对象
* @return 苹果对象
*/
@Override
public Apple getFruit() {
return new Apple();
}
}
/**
* 橘子工厂
*/
public class OrangeFactory extends FruitFactory<Orange> {
/**
* 获取橘子对象
* @return 橘子对象
*/
@Override
public Orange getFruit() {
return new Orange();
}
}
这样,就可以使用不同类型的工厂来生产不同类型的水果了,如果新增了水果类型,直接创建一个新的工厂类就行,不需要修改之前已经编写好的内容。
// 使用对应的工厂生产对应的对象
Apple apple = new AppleFactory().getFruit();
Orange orange = new OrangeFactory().getFruit();
这样,就简单实现了工厂方法模式,通过工厂来屏蔽对象的创建细节,使用者只需要关心如何去使用对象即可。
