静态工厂模式是一种改进的获取实例的方法。
通常我们会使用new关键字调用类的构造方法来创建一个对象,静态工厂模式相对于传统的创建对象的方式有以下优点:
1. 可以更加富有语义的创建实例:当一个类的构造方法有非常多的参数或被重载过很多次的话,因为JAVA对构造方法命名的规定(与类名相同),我们必须编写多个命名相同但实际不同的构造函数,在创建对象时很难区分我们应该调用哪个构造方法。
比如在实际生产中,我们会经常见到以下方法:
newInstance():获取一个新的对象
valueOf():获取一个值为..的对象
getInstance():获取一个对象缓存池中的对象或单例对象
甚至我们可以更加细分,比如有一个Person类,我们想根据年龄来获取不同的对象,则可有如下静态工厂方法:
getChild():获取一个儿童对象
getOld():获取一个老年对象
getYouth():获取一个青年对象
等等,比起new关键字,显然使用上述方法获取对象更加具有可读性,使我们对创造的实例类型更可控。
2. 不必每次调用都创建新的对象
当一个类的对象会被频繁使用,且没有必要在每次使用时都生成新的对象时,我们会考虑使用单例模式。单例模式大多是由静态工厂实现的,我们可以在工厂内部控制新生成实例或返回已有实例。
比如,DCL单例模式获取对象时就是采用了静态工厂:
public class Car { //构造函数私有,禁止通过常规方式实例化 private Car(){} //单例对象的引用 static volatile Car car=null; //DCL获取单例对象,静态工厂方法 static Car getInstance(){ if(car==null){ synchronized(Car.class){ if(car==null){ car=new Car(); } } } return car; } private Object readResolve() { return getCar(); } }
3. 可以返回原返回类型的子类
我们可以通过静态工厂返回一个类型的所有子类,可以更加灵活的获取实例。这也符合了两大设计原则:里氏替换原则与依赖倒置原则。即所有父类可以出现的地方子类也可以出现,以及类的使用方不应该依赖具体的实现类,而应该依赖继承链的顶端(接口或抽象类),即依赖抽象。
从Car中我们可以获取Bus和Taxi两个子类的实例,其它类在使用时只需维护一个Car的引用,至于具体用Bus还是用Taxi只由获取实例的那行代码决定,我们可以在只更换实例的获取而不改变其它代码(业务逻辑)的情况下修改代码。
public class Car{ public static Car getBus(){ return new Bus(); } public static Car getTaxi(){ return new Taxi(); } } public class bus extends Car{ } public class taxi extends Car{ }
4. 在创建带泛型的参数时,使代码更简洁
这条主要是针对带泛型类的繁琐声明而说的,需要重复书写两次泛型参数:
Map<String,Date> map = new HashMap<String,Date>();
不过自从 java7 开始,这种方式已经被优化过了 —— 对于一个已知类型的变量进行赋值时,由于泛型参数是可以被推导出,所以可以在创建实例时省略掉泛型参数。
Map<String,Date> map = new HashMap<>();
所以这个问题实际上已经不存在了。
总的来说,静态工厂模式便是把创建实例的任务由使用方移到了提供方,基于提供方比使用方更了解自己的事实,这可以使对象的创建更加合理和可控。在实际生产时,我们应该优先考虑通过静态工厂模式获取实例。