在java开发中,创建并初始化对象是再平常不过的一件事了,大部分情况下,我们都会选择用new构造符去创建一个新的对象,但是在开发中我们也常常会遇到一些特殊的初始化方式,随举一例,比如在hibernate中,创建session并不是用new构造符去初始化的,往往我们会从sessionFactory实例中去获取session,而这种通过factory获取实例的初始化方式,我们称为工厂模式。
事实上工厂模式是可以细分成好多种类的,有“简单工厂模式”、“工厂方法模式”、“抽象工厂模式”,在G4整理的设计模式中也将工厂方法和抽象工厂分为两种不同的模式,关于这三种模式之间的区别,我借用网上一篇博客来说明:
博客地址:https://www.cnblogs.com/zhangchenliang/p/3700820.html
具体的讲解和例子请自行去阅读这篇博客,这篇博客以一个相对比较通俗易懂的例子来说明了一下几种模式的区别,这里我稍微概述一下。
简单工厂模式是工厂模式的最基本实现,工厂模式原本就是想要把创建实例的过程从调用方抽离出来,简单工厂模式就是把每个需要创建实例对象的类都设置一个创建实例的方法,调用方去调用这些方法。
public class Factory {
public Object1 create1() {
return new Object1();
}
public Object2 create2() {
return new Object2();
}
}
但是这样的弊端是,万一我想加个Object3,就不得不去修改factory类,为了应对这个方式,就在简单工厂上加上了接口声明多态实现的过程。
public interface Factory<T> {
public T create();
}
public class Factory1 implements Factory<Object1> {
@Override
public Object1 create() {
return new Object1();
}
}
这样当需要加入Object3时就只需要重新写个Factory3去实现Factory接口即可。
但是这样是类族相对比较单一的时候,万一类的继承比较复杂,如何设计这个工厂类,是每个实例都创建一个工厂还是给类族归类去创建一个层次上的工厂,这就是工厂方法模式到抽象工厂模式的进阶。(这个就不举例了,看上面那个链接的博客去吧)
好,现在开始进入正文,首先先来谈谈我对工厂模式的看法。
在网上各种博客中,对工厂模式褒贬不一,有些人认为引用创建本来就是调用方的职责,将这个过程从调用方抽离不符合高内聚的原则;也有人认为,如果一个类的new构造符被大量的使用,在这个时候被调用方想要修改创建的逻辑就会非常被动,所以抽离出来降低了耦合性。
其实我个人觉得这两种说法都不是非常妥当,首先前一种说法过分钻牛角尖,工厂模式并不是否定用new构造符去创建对象的可行性,而是在一些情况下,用new构造符去创建会有大量的其他工作,对于调用方来说是个比较繁复的工作,比方说前文提到的hibernate的sessionFactory,它完成了数据源的初始化,并创建数据库操作的session,相比之下如果要由调用方自己去new一个session,再从静态变量中获取DataSource传给session,这个过程的复杂程度远高于用工厂模式,因此用工厂模式并没有降低内聚,反而在某些情况下降低了耦合。
而后一种论调的问题在于,忽略了工厂模式在抽象化上的作用。事实上如果单纯依照这种看法,我们大可以在构造函数中完成逻辑的变更,如果是传入参数发生变化,用工厂模式也很难起到很好的作用;个人认为工厂模式在多方调用中一个更大的优势就是其抽象性,尤其是工厂方法模式,当你已经得到一个工厂的时候,无需知道需要创建的实例本身是个啥,只需要用这个工厂去创建实例即可。
这个论点乍一看好像很扯淡,因为在工厂方法模式中,如果你都已经去确定工厂到底是哪一个了,这和确定需要创建的实例究竟是哪个类有什么区别?起初我也是这么认为的,直到看到一个观点,java集合类的iterator方法就是一个工厂模式,我才恍然大悟。
在java的集合类接口Collection中,定义了一个创建迭代器的方法iterator
/**
* Returns an iterator over the elements in this collection. There are no
* guarantees concerning the order in which the elements are returned
* (unless this collection is an instance of some class that provides a
* guarantee).
*
* @return an <tt>Iterator</tt> over the elements in this collection
*/
Iterator<E> iterator();
而迭代器接口中则定义了迭代遍历集合的方法
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();
}
在调用的过程中:
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
System.out.println(iterator.next());
}
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
System.out.println(iterator.next());
}
我们很容易发现,无论我使用的是List还是Set,无论我用的是ArrayList还是Vector,我都可以统一地使用iterator方法生成对应的迭代器进行遍历,当然在jdk1.4还不知道1.5之后,对于这样的遍历我们可以简单使用for(String s : list)的形式,但是从迭代器的例子中我们也很容易意识到工厂方法模式的一个实质作用,当我们已经获取到工厂类的对象的时候可以统一生产对应的实例。当工厂类和实例类的类族非常复杂的时候,统一的创建接口会大大减少开发成本,且不易出错。
前两天一位同学提到了JavaScript中的抽象工厂模式:
/**
*抽象工厂
* @param subType 传入的是一个对象
* @param superType 传入一个字符串
* @constructor subType
*/
var VehicleFactory = function(subType,superType){
console.log(VehicleFactory.Car);
if(typeof VehicleFactory[superType] === "function"){
function F(){};
F.prototype = new VehicleFactory[superType]();
subType.constructor = subType;
subType.prototype = new F();
} else{
throw new Error("没有创建抽象类");
}
}
/*一个抽象类*/
VehicleFactory.Car = function(){
/*构造函数 存放属性*/
this.type = "car";
}
VehicleFactory.Car.prototype = {
getType:function(){
throw new Error("不可调用getPrice()");
}
}
/*实现抽象类*/
/*宝马车子类*/
var BMW = function(type){
this.type = type;
}
VehicleFactory(BMW,'Car');
/*
BMW.prototype.getType = function () {
return this.type;
}
*/
var a = new BMW("yay");
console.log(a.getType());
刚看到这段代码的时候我略微有点小懵逼,这段代码不难懂,但是我怎么也没法理解这为什么算是工厂模式,在G4总结的设计模式中,工厂模式是一种对象创建型的模式,然而这段代码分明是实现了继承么,怎么就成了工厂模式了。
但是仔细想想,也并非毫无道理,这里我说下我个人片面的见解,还记得之前我写过的博客,一切都是对象吗?js本身就有着这种奇特的特性,因此在JS中区分创建的是对象还是类,是没有特别大的意义的,这里我们可以把“构造函数”这个事物当做一个类,而具体的构造函数则是一个对象,事实上JS中构造函数本身就是个对象,那这个工厂模式就实现了初始化一个“构造函数”的时候,给定一些初始值。而这种工厂构造最终实现了在JS中继承的作用。
我一直做这样的比喻:如果写代码是打仗,设计模式就是兵书。熟读理解兵书兵法可以用助于自己排兵布阵,但是过分执着于书本内容而不活学活用就容易落得和马谡一样的下场。现阶段我对设计模式的理解是比较浅显的,而设计模式的书则是前人在自身开发经验的总结,随着个人能力的提升,去回顾书本内容自然会获得不同的启发。