静态工厂方法代替构造器
1 静态工厂方法 与 构造器 是什么?
假设我们有一个学生类Animal
- public class Animal{
- //your code
我们获取这个类的示例 最常用的方式就是使用这个类公有的构造器,每个类默认会有一个无参的构造器
- public class AnimalTest {
- public static void main(String [] args){
- new Animal();
还有另外一种方法,也就是我们需要了解的静态工厂方法(static factorymethod),它是一个返回类实例的静态方法。那么我们就给Student类一个静态工厂方法
- public class Animal{
- private static Animal s = new Animal();
- public static Animal getInstance(){
- return animal;
- public class AnimalTest {
- public static void main(String [] args){
- new Animal();
说明这里面的静态工厂方法并不是设计模式中的工厂模式
2 静态工厂方法 与 构造器 的对比
我们可以通过类的静态工厂方法来获得示例,而不是单独是构造器。
2.1 使用 静态工厂方法 的优点
2.1.1 静态工厂方法是有名称的
与构造器相比,静态工厂方法是有名称的。 如果构造器的参数不能够明确的描述返回的对象,我们使用有名称的静态工厂方法就是比较好的。我们可以通过名称的含义来理解这个静态工厂方法返回类的示例。
这种方法尤其在一种情况下很突出,当一个类只能有一个带有签名的构造器。我们都是通过修改两个构造器中的参数列表中的参数类型顺序。面对这样的API 使用起来是很麻烦的,由于静态工厂方法 是有名称的,所以不受这个限制。
2.1.2 不必每次调用都创建新的对象
使用静态的工厂方法,我们可以预先构造好类的实例,或者将写好的实例放入缓存中,进行重复的利用。
静态的工厂方法能够进行重复的调用而返回相同的对象,这可以帮助类控制那些时刻哪些实例应该存在。这种类型的类被称为 示例受控制的类(instance-controlled),写这样的类有几个原因:
确保这个类是单例模式(Singleton) 或者不可实例化的
可是使用 "==" 操作符来代替 equals(Object)方法,提高性能
- public class AnimalTest {
- public static void main(String [] args){
2.1.3 可以返回类子类的对象 *
这个特性是非常灵活的,我们先试图想象三个组件 服务接口(用于用户获得实例)、 提供者注册API(服务器注册的实例)、 服务访问API(用于用户访问,获取) .其中还有一个可选的组件 服务提供者接口(用于创建实例)。这个就是基本的服务提供者框架,它也可以变化为 适配器(Adapter) 模式
说明:如果有web开发经验的可以把这个当作一个用户拿不同的数据访问 action ,而action 返回不同的视图。而第四个组件我们可以想象成一个Spring 的 base 实例化 action 对象。
这里提供一个完整的代码,希望大家能够自己拿去运行一下、拓展一下。
- /**
- *服务访问API
- */
- public interface IAnimal {
- /**
- *服务提供者接口
- */
- public interface IProvider {
- IAnimal ianimal();
- public class Animal{
- //构造方法私有化,客户端不能够使用构造器获得对象
- private Animal(){}
- //根据名称放入相应的实例
- private static final Map<String,IProvider> providers= new ConcurrentHashMap<String,IProvider>();
- public static final String DEFAULT_IPROVIDER_NAME = "<dfe>";
- //用户注册实例的接口 提供者注册API
- public static void registerDefaultProvider(IProvider p){
- public static void registerProvider(String name,IProvider p){
- //用户获得实例的接口 服务接口
- public static IAnimal newInstance(){
- return newInstance(DEFAULT_IPROVIDER_NAME);
- public static IAnimal newInstance(String name){
- if(p == null)
- throw new IllegalArgumentException(
- "没有实例注册这个名称:"+name);
- return p.ianimal();
- public class Provider implements IProvider{
- private IAnimal iAnimal;
- public Provider(IAnimal iAnimal){
- this.iAnimal = iAnimal;
- @Override
- public IAnimal ianimal() {
- return iAnimal;
- public class Person implements IAnimal{
- @Override
- public String toString() {
- return "Person 实例";
- public class Dog implements IAnimal{
- @Override
- public String toString() {
- return "Dog 实例";
- public class AnimalTest {
- public static void main(String [] args){
- "dog", new Provider(new Dog()));
- "person", new Provider(new Person()));
- "dog");
- "person");
2.1.4 创建参数化类型实例更加简洁
在创建参数化类型实例的时候,静态工厂方法 会让代码变得更加的简洁
在调用参数化的构造器的时,即时类型参数很明显,也必须指明。通常要接连提供两次类型参数
Map<String,List<String>> m = new HashMap<String,List<String>>();
这个类型的声明太过于冗长,假设HashMap 有一个这样的静态工厂方法:
- public static <K, V> HashMap<K, V> newInstance(){
- return new HashMap<K, V>;
这样我们就可以这样声明HashMap 了
Map<String,List<String>> m = HashMap.newInstance()
2.2 使用 静态工厂方法 的缺点
2.2.1 可能类不能实例化
类如果实现静态的工厂方法,而不含有公有的或者受保护的构造器,就不能够被实例化。这也鼓励我们多使用 复合(composition) 而不是继承
2.2.2 与其他的静态方法没有区别
静态工厂方法在API 文档中没有像构造器一样明确的表示出来,所以,对于静态工厂方法而不是构造器的类来说,想要看清楚是一件不简单的事情。在javadoc工具没有注意到静态工厂方法前,我们通过命名来区分:
valueOf -- 这个方法返回的实例与它的参数具有相同的值,这样的静态工厂方法实际上是用来做类型转换的。
of -- valueOf的简写,在EnumSet 开始使用流行
getInstance -- 返回的实例是通过方法的参数来描述的,但是不能够说与参数具有相同的值。对于Singleton来说这个方法没有参数,并返回唯一的实例
newInstance -- 像getInstance一样,但newInstance确保返回的每个实例都与其他实例不同。
getType -- 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。type表示工厂方法所返回的对象类型
newType -- 像newInstance一样,但是在工厂方法处于不同的类中的时候使用。type表示工厂方法所返回的对象类型
3 总结
静态工厂方法和公有构造器是各有用处的,但是我们需要理解他们的长处。静态工厂方法通常更加的合适,希望我们以后第一时间不是考虑构造器,而是想想静态工厂方法。