静态工厂方法和工厂模式很像,但是在java中还是有很大区别的。
(一)静态工厂方法vs工厂模式
对于一个类来说,为了让客户端获得一个自身的实例,最常用的方法就是提供一个公有的构造器。除了这种使用构造器的方法之外,对于单个类来说,我们可以定义静态工厂方法来获取自身的类的一个实例。静态工厂方法和工厂模式的不同也体现在此,静态工厂方法是获取这个类自身的一个实例,他的存在是为了更好的描述和处理这个类。而工厂模式的作用更在于解耦,让每个类在实例化的时候不再使用new这种耦合度极高的方法。
我们通过静态工厂方法来代替构造器,我们首先需要知道的是静态工厂方法只是一个“普通的方法”。的确,我们将具有:返回这个对象的一个实例这种特点的静态方法叫做静态工厂方法,它在理论上与其他方法并没有什么不同。但是也正是因为他是一种”普通的方法“,它具有很多构造器不具备的优点。
工厂方法又分为三种模式:简单工厂模式、工厂模式(工厂方法模式)、抽象工厂模式。这三种是为了一个目标的不同程度的抽象。工厂方法用一种比静态工厂方法更加系统的理论来对每一个需要使用的对象进行包装。这种包装下再也不使用new来创建对象,随着抽象程度的变高,我们甚至可以将这种创建对象放到xml或者注释之中——就像是那么多框架做的一样。
总而言之,静态工厂方法在一个类的内部,较小的范围里可以让你创建对象更加方便优雅,而工厂模式在大的范围中能够让你的代码重用性更佳,耦合度更低。
(二)静态工厂方法可以有不同的名字
对于普通的构造器来说,通过参数来对类中的不同属性赋值,然后返回一个这个类的相应实例。但在有些时候,如果我们想获得有些差别的类实例,唯一可以采用的方法是通过不同的构造器参数列表来区分不同的实例。但是这并不是一个好主意,因为有的时候,仅仅是构造器方法签名上的不同,可能会让客户端用户迷惑,只是参数顺序的变化让他们很难去记住到底哪个构造对应的是哪个实例。
这个时候可以考虑使用静态工厂方法,为不同的构造方法来起不同的名字来区分不同功能的实例化,而返回的都是一个this,这样类似构造器的操作让不同的实例可以被更加容易的区分。
(三)静态工厂方法可以返回一个现有的实例
每当我们调用构造器,每当我们使用new来初始化一个对象时,都无疑是在堆上创建了一个新的对象。而有些不可变的类、不希望被实例出多个对象的类不用也不必每次都创建一个对象。通过静态工厂方法,我们可以直接向客户端返回一个我们早已创建好的对象,对于有些不可变的类,比如基本类型包装类,这样做可以极大地节省我们的开销。
对于单例模式的类,只允许每个类中存在一个已经实例化的对象。对于单例模式来说,构造器都会被声明为private,我们不能也无法构造一个对象,只能通过静态工厂方法来获取他的对象。这种手法在很多很多包中都有体现,尤其是那些比较重量级的类(其实我们也恰好总喜欢把这种变量其名为factory),更需要这种手法来操作对象。
(四)静态工厂方法可以作为视图返回子类型
视图这个名词有很多解释,在这里视图的解释是java集合框架中的视图方法——通过返回一个List(或者其他具体类型的祖先类,Set,Map等)对象,让那些List的子类都可以来调用这个方法。
(五)简单工厂方法
相比较静态工厂方法可以便利一个类的实例化创建过程,工厂方法在一种模式的角度上,对所有需要new来实例化的类进行封装解耦。提到过工厂方法分为三种:简单工厂模式、工厂模式(工厂方法模式)、抽象工厂模式。这三种模式只是一种思维上的不断抽象。我们先从简单工厂方法来看,从简单的入手。
package FactoryEx;
/**
*
* @author QuinnNorris
*
* 简单工厂方法
*/
public class SimpleFactory {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Apple apple = new Apple();
Banana banana = new Banana();
}
}
class Apple {
}
class Banana {
}
class Factory {
}
在没有工厂方法的时候,我们创建一个类对象是通过这种new的方式,这样的好处在于简洁。但是如果我们在做一个很大很大的项目的时候,需要对项目中所有的Apple对象都改成Banana对象,这个时候就炸了,因为要去查找所有的Apple对象一个个手动修改。我们现在要用工厂方法来取缔这种代码紧密的结合的情况。
package FactoryEx;
/**
*
* @author QuinnNorris
*
* 简单工厂方法
*/
public class SimpleFactory {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Factory factory = new Factory();
factory.FruitFactory("apple");
}
}
/**
*
* @author QuinnNorris
*
* 各种水果的祖先类,我们要实例化产品的抽象类
*/
abstract class Fruit {
}
/**
*
* @author QuinnNorris
*
* 苹果,我们要实例化的一种具体产品
*/
class Apple extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 香蕉,我们要实例化的一种具体产品
*/
class Banana extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 工厂类,这个类中决定最后给你一个苹果还是香蕉
*/
class Factory {
/**
*
* @param fruit
* 通过这个参数表达你的需求
* @return 根据你的需求返回相应的水果
*/
public Fruit FruitFactory(String fruit) {
if (fruit.equals("apple"))
return new Apple();
if (fruit.equals("banana"))
return new Banana();
return null;
}
}
简单工厂方法分为这么几个部分:
- 抽象产品类:所有具体产品的抽象祖先,这个类的存在是为了在工厂类中可以返回这个类的子类提供方便。
- 具体产品类:我们需要实例化的不同的产品对象。
- 工厂类:通过工厂类,我们在“工厂”中按照要求打包不同的“水果”发货送到“顾客”手中。
- 客户端类:客户端表达自己的需求,从工厂中获取水果。
通过这种机制,我们获得一个水果对象的具体过程不必再使用new,而是:
Factory factory = new Factory();
factory.FruitFactory("apple");
我们先创建一个工厂对象,如果你觉得这会让我们创建两个对象,我建议使用静态方法。在创建了工厂之后,通过调用工厂方法传入参数获取我们想要的水果。在第二句中,返回值是一个Apple类型对象,我们可以直接在代码中当作apple使用它,如果我们想要将Apple换成Banana,只需要将参数变换成其他的。甚至,我们可以将这个参数提取出来,放到xml文档,或者其他存储信息的地方便于信息的更改。这里实例化出的类可以随时改变,这样不会产生new出来无法改变的局限性。
这就是简单工厂方法,用起来很舒服,增加了扩展性,又没有去修改代码。但是简单工厂方法存在的一个问题是:每当多一种水果,工厂类的方法中就要多一种if选择情况,如果在数量非常多,实现内容非常长的情况下,这无疑让整个类变得非常臃肿。为了解决这个问题——工厂方法模式出现了。
(六)工厂方法模式
工厂方法模式是简单工厂模式的一种升级和抽象。工厂方法模式的核心逻辑是:通过将工厂类采用继承的方式细分减轻在简单模式中工厂类的压力。通过不同的工厂来分别管理不同的产品,一种类似树形的划分方式,让代码更加有可读性。
package FactoryEx;
/**
*
* @author QuinnNorris
*
* 工厂方法模式
*/
public class FactoryMethord {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Factory appleFactory = new AppleFactory();
appleFactory.FruitFactory();
Factory bananaFactory = new BananaFactory();
bananaFactory.FruitFactory();
}
}
/**
*
* @author QuinnNorris
*
* 各种水果的祖先类,我们要实例化产品的抽象类
*/
abstract class Fruit {
}
/**
*
* @author QuinnNorris
*
* 苹果,我们要实例化的一种具体产品
*/
class Apple extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 香蕉,我们要实例化的一种具体产品
*/
class Banana extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 抽象工厂类,所有具体的工厂类的子类
*/
abstract class Factory {
public abstract Fruit FruitFactory();
}
/**
*
* @author QuinnNorris
*
* 具体的工厂类,分管苹果这个产品
*/
class AppleFactory extends Factory {
@Override
public Fruit FruitFactory() {
return new Apple();
}
}
/**
*
* @author QuinnNorris
*
* 具体的工厂类,分管香蕉这个产品
*/
class BananaFactory extends Factory {
@Override
public Fruit FruitFactory() {
return new Banana();
}
}
工厂方法模式分为这么几个部分:
- 抽象产品类:所有具体产品的抽象祖先,这个类的存在是为了在工厂类中可以返回这个类的子类提供方便。
- 具体产品类:我们需要实例化的不同的产品对象。
- 抽象工厂类:所有具体工厂的抽象祖先,我们在这个祖先中定义工厂方法。
- 具体工厂类:通过具体的工厂类来分别获取不同的水果。
- 客户端类:客户端表达自己的需求,从工厂中获取水果。
可能在这个只有两种水果的例子中使用工厂方法模式非常的蠢,但是这种模式在要实例化的内容很多的时候还是非常方便的。有的时候我们所有的需实例的类太多,或者整个框架中的类是一种树形框架的时候,这种工厂方法模式能发挥它最大的作用。
(七)抽象工厂模式
抽象工厂模式和实际上是工厂方法的一种更加泛化的情况。在抽象工厂模式中,抽象产品可能是一个或多个,从而构成一个或多个产品族。 在只有一个产品族的情况下,抽象工厂模式就是工厂方法模式。
package FactoryEx;
/**
*
* @author QuinnNorris
*
* 抽象工厂方法
*/
public class AbstractFactory {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Factory aFactory = new AppleEnglishFactory();
aFactory.FruitFactory();
aFactory.BookFactory();
Factory bFactory = new BananaMathFactory();
bFactory.FruitFactory();
bFactory.BookFactory();
}
}
/**
*
* @author QuinnNorris
*
* 各种书籍的祖先类
*/
abstract class Book {
}
/**
*
* @author QuinnNorris
*
* 从书籍抽象类继承下来的子类,一种具体的产品
*/
class EnglishBook extends Book {
}
/**
*
* @author QuinnNorris
*
* 从书籍抽象类继承下来的子类,一种具体的产品
*/
class MathBook extends Book {
}
/**
*
* @author QuinnNorris
*
* 各种水果的祖先类,我们要实例化产品的抽象类
*/
abstract class Fruit {
}
/**
*
* @author QuinnNorris
*
* 苹果,我们要实例化的一种具体产品
*/
class Apple extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 香蕉,我们要实例化的一种具体产品
*/
class Banana extends Fruit {
}
/**
*
* @author QuinnNorris
*
* 抽象工厂类,所有具体的工厂类的子类
*/
abstract class Factory {
public abstract Fruit FruitFactory();
public abstract Book BookFactory();
}
/**
*
* @author QuinnNorris
*
* 具体的工厂类,分管苹果和英语书这两个产品
*/
class AppleEnglishFactory extends Factory {
@Override
public Fruit FruitFactory() {
return new Apple();
}
@Override
public Book BookFactory() {
return new EnglishBook();
}
}
/**
*
* @author QuinnNorris
*
* 具体的工厂类,分管香蕉和数学书这两个产品
*/
class BananaMathFactory extends Factory {
@Override
public Fruit FruitFactory() {
return new Banana();
}
@Override
public Book BookFactory() {
return new MathBook();
}
}
上面的例子和工厂方法模式很相似,但是不同在于在抽象工厂中,每个工厂的工作又多了一些:现在每个工厂不仅仅只负责给顾客(客户端)打包送水果,而且还要给顾客发教科书。而且水果和书是完全来自两个抽象类的,那么我们把这种一个工厂涉及到多个产品组的模式称作抽象工厂。在只有一个产品族的情况下,抽象工厂模式就是工厂方法模式。