工厂方法模式
(Factory Method)
中文定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例延迟到其子类。
英文定义:
Define an interface for creating an object, But let’s subclasses decide which class to instantiate. Factory Method let’s a class defer instantiation to subclasses.
类图:
- 抽象工厂(Factory)角色:担任这个角色的是工厂方法模式的核心,与应用无关。任何在模式中创建对象的工厂类必须实现这个接口。
- 具体工厂(Concrete Factory)角色:担任这个角色的是实现了抽象方法接口的具体类。具体工厂角色与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
- 抽象产品(Product)角色:工厂方法模式创建对象的超类,也就是产品对象的共同父类或共同拥有的接口。
- 具体产品(Concrete Product)角色: 这个角色实现了抽象产品角色所声明的接口。工厂方法所创建的每一个对象都是某个具体产品的角色的实例。
解决场景:
根据调用者的需求由工厂方法来负责创建不同的对象,调用者不需要关心创建对象的细节,工厂创建过程要坚持对修改关闭,对扩展开发原则。如日志记录器,需要记录到本地的对象、记录到数据库对象、记录到远程服务器对象,通过工厂方法来创建三种记录对象。
核心思路:
基于Java语言多态的特性,在接口中定义一个创建对象的抽象方法,子类继承或实现接口负责对象的创建。
优点:
- 符合开闭原则,增加一个产品类,只需要实现具体产品类和具体工厂类;
- 符合单一职责原则,每个工厂只负责创建对应的产品;
- 使用者只需要要知道具体产品,无需要关心怎么创建
缺点:
- 增加一个产品时,需要实现对应的具体产品类和具体工厂类,使得系统类的数据增加;
- 只能对同类型的对象进行扩。
补充:
工厂方法模式和简单工厂模式在结构上的不同是很明显的。工厂方法模式的核心是一个抽象类,而简单工厂模式把核心放在一个具体类上。工厂方法模式可以允许很多具体工厂类从抽象工厂类中将创建的行为继承下来,从而可以成为多个简单工厂模式的综合,进而推广了简单工厂模式。
工厂方法模式退化后可以变得很像简单工厂模式。设想如果非常确定一个系统只需要一个具体工厂类,那么就不妨把抽象工厂类类合并到具体的工厂类中去。由于只有一个具体工厂类,所以不妨将工厂方法改为静态方法,这时候就得到了单例工厂模式。
与简单工厂模式中的情形一样的是,ConcreteCreator的factory方法返还的数据类型是一个抽象类型Product,而不是哪个具体产品类型,而客户端也不必知道所得到的产品的真实类型。这种多态性设计将工厂类选择创建哪一个产品对象、如何创建这个对象的细节完全封闭在具体工厂类内部。
工厂方法模式之所以有一个别名叫多态性工厂模式,显然是因为具体工厂类都有共同的接口,或者都有共同的抽象父类。
如果系统需要加入一个新的产品,那么所需要的就是向系统中加入一个这个产品类以及它所对应的工厂类。没有必要修改客户端,也没有必要修改抽象工厂角色或者其他已有的具体工厂角色。对于增加新的产品类而言,这个系统完全支持"开—闭"原则。
拓展:
参数工厂方法,如果一个类产品对应一个工厂类,这样系统会会变得臃肿复杂。我们可以对产品进行分类,如水果按照南方,北方进行分类,这样可以定义南方水果工厂、北京水果工厂。南方水果工厂生成南方苹果、南方葡萄等,北京工厂生成北京苹果、北京葡萄等。这样缺点是增加一个类型水果需要修改工厂类、破坏“开闭”原则。
我们也可以通过反射的方式:
通过Class.forName().newInstance()来获取对象,缺点是产品必须是无参数构造函数。
public abstract class FruitFactory {
public abstract <T extends Fruit> T factory(Class<T> claz);
}
public class NorthFactory extends FruitFactory {
@Override
public <T extends Fruit> T factory(Class<T> claz) {
try {
return (T)Class.forName(claz.getName()).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
Class.forName()、Class.forName().newInstance() 、New 三者区别
- newInstance( )是一个方法,而new是一个关键字;
- Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用 new关键字生成对象没有这个限制;
- newInstance(): 弱类型,低效率,只能调用无参构造;
- new: 强类型,相对高效,能调用任何public构造;
- Class.forName(“”)返回的是类;
- Class.forName(“”).newInstance()返回的是object。
class.forName和classloader的区别
- 加载:java类运行时候会生成一个class字节码文件,加载的过程就是去我们的操作系统寻找这个class文件;
- 链接:这个过程就是把class文件加载到java虚拟机;
- 初始化:在虚拟机中根据class文件进行初始化;
- 使用:这个过程大家都明白;
- 卸载:使用完了,java虚拟机进行清理。
区别:
- class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。注意这里的静态块指的是在类初始化时的一些数据;
- 底层还是使用的classloader加载;
- 默认执行static的代码即静态代码;
- classloader并没有初始化静态块。
综上所述,class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。当然还可以指定是否执行静态块;classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(String name)->
Class.forName(className, true, ClassLoader.getClassLoader(caller))->
Class.forName0(String name, boolean initialize, ClassLoader loader)->
ClassLoader.loadClass()