在介绍工厂方法模式的时候,我们先介绍一个不是设计模式,但是用途却不必工厂方法模式少的静态工厂模式。
1、静态工厂
通常在java中我们创建一个对象的时候都是使用new的方式来创建对象的,像下面这样:
Date date = new Date();
//Fragment为自定义的类
Fragment fragment = new Fragment();
但是我们也会经常看到下面的方法
Calendar calendar = Calendar.getInstance();
Integer number= Integer.valueOf(2);
而下面这种通过直接使用类调用方法的就是静态工厂方法。如果我们点开valueOf的源码查看的话,可以看到其实他就是将这个方法加了static字段,我们知道static的方法不用实例来调用,而是直接使用类来调用的。
那么这样做有上面好处呢,在< Effective Java>中作者总结了一下几点:
(1)静态工厂方法相比较于构造器创建方法他们有名称
传统的我们如果创建多个具有不同参数的对象的时候,都是通过重载的方式,也就是每个方法都具有跟类名一样的名字。比如下面这个代码
Date date0 = new Date();
Date date1 = new Date(0L);
Date date2 = new Date("0");
Date date3 = new Date(1,2,1);
Date date4 = new Date(1,2,1,1,1);
Date date5 = new Date(1,2,1,1,1,1);
这样当你在创建不同参数的对象的时候是不是把你看得眼花缭乱的,但是使用静态工厂方法就不一样了,通过上面的那个Integer.valueOf,可以看出我们可以使用具有意义的名称来创建对象。
(2)不用再每次调用它们的时候都创建一个新对象
这个功能就强大了,因为这个方法是用static字段声明的,说明它只在类加载的时候创建一次,之后再也不创建了。这样就减少了多次调用而重复创建对象,而这一个特性也可以用在SingleTon的创建,具体看这里
(3)它们可以返回原返回类型的任何子类型的对象
这条不用多说,设计模式中的基本的原则之一——『里氏替换』原则,就是说子类应该能替换父类。
显然,构造方法只能返回确切的自身类型,而静态工厂方法则能够更加灵活,可以根据需要方便地返回任何它的子类型的实例。比如下面这个方法:
Class Person {
public static Person getInstance(){
return new Person();
// 这里可以改为 return new Player() / Cooker()
}
}
Class Player extends Person{
}
Class Cooker extends Person{
}
比如上面这段代码,Person 类的静态工厂方法可以返回 Person 的实例,也可以根据需要返回它的子类 Player 或者 Cooker。(当然,这只是为了演示,在实际的项目中,一个类是不应该依赖于它的子类的。但如果这里的 getInstance () 方法位于其他的类中,就更具有的实际操作意义了)
除了以上的在作者总结的几个,如果你觉得还不够酷,可以看看这个为什么要用静态工厂
当然静态工厂还有一个版本(为了引出工厂方法模式),再来举个栗子。
场景:比如我们要生产男孩子和女孩子(比喻好像有点怪怪的,相当于造娃吧),但是后序还有可能想生出个像男像女的娃现在我们考虑用下面的方式
public class Test {
//定义实现类接口
interface Person{
public abstract void say();
}
//具体实现 -男人类
class Man implements Person{
@Override
public void say(){
System.out.println("我是男人");
}
}
//具体实现 -女人类
class Woman implements Person{
@Override
public void say(){
System.out.println("我是女人");
}
}
//简单工厂
class SimpleFactory{
public Person createPerson(int type){
Person person = null;
if(type == 1){
person = new Man();
}
if(type == 2){
person = new Woman();
}
return person;
}
}
public static void main(String[] args) {
//实例化工厂对象
SimpleFactory factory = new Test().new SimpleFactory();
//传递约定参数1,返回Man 对象
Person person = factory.createPerson(1);
person.say();
//传递约定参数2,返回Woman 对象
person = factory.createPerson(2);
person.say();
}
}
这次我们创建了一个可以生男孩和女孩的工厂,而具体的男孩和女孩的创建交给了工厂去做。在客户端调用的时候,只需要传入我们所需要的人就可以,其他的交给服务器工厂去做。这样就可以减轻客户端的压力,将创建的过程交给了服务端。当我们想要再创建一个像男像女的时候,只需在实现Person接口,然后将其加入到工厂当中就可以了。
从上面的两个例子我们都可以看出,静态工厂方法跟传统的方法都是不直接使用new来创建对象,而是将这个步骤给"隐藏"起来。
前面说了那么多都是静态工厂方法的使用和说明,现在是时候开始用工厂方法模式了,如果你理解了上面第二个静态工厂,那么工厂方法模式就简单多了。
首先上面的静态工厂方法是
将自行车和轿车的制造交给工厂来处理,而工厂方法模式是在静态工厂方法之上进一步进行了高度抽象。
这相当于一个什么样的概念呢,就是静态工厂方法是一个公司,这个公司可以有很多业务部门,而工厂方法模式是一个集团,这个集团下面有很多公司,各个公司又有很多个部门。
我们对比一下上面的例子来看下面这个例子
public class Test {
//接口
interface Person{
public abstract void say();
}
//具体实现 -男人类
class Man implements Person{
@Override
public void say(){
System.out.println("我是男人");
}
}
//具体实现 -女人类()
class Woman implements Person{
@Override
public void say(){
System.out.println("我是女人");
}
}
//简单工厂接口
interface SimpleFactory{
Person getPerson();
}
//男人工厂类
class ManFactory implements SimpleFactory{
@Override
public Person getPerson(){
return new Man();
}
}
//女人工厂类
class WomanFactory implements SimpleFactory{
@Override
public Person getPerson(){
return new Woman();
}
}
public static void main(String[] args) {
//实例化男人工厂类
SimpleFactory manFactory = new ManFactory();
Person man = manFactory.getPerson();
man.say();
//实例化女人工厂类
SimpleFactory womanFactory = new WomanFactory();
Person woman = womanFactory.getPerson();
woman.say();
}
}
工厂方法其实是简单工厂的高度抽象。将简单工厂中的工厂类,进一步抽象出创建实例的接口,而简单工厂中的实例化对象方法则被演变成了实现创建实例接口的子类工厂类。看似是将实例化对象的过程复杂化了,其实我们是将实例化对象的过程不断分层,降低耦合,对扩展开放,对修改封闭从而提高了我们代码的可维护性和扩展性。