ybbzbb
一步一步的走

静态工厂方法代替构造器

1 静态工厂方法 与 构造器 是什么?

假设我们有一个学生类Animal

  1. public class Animal
  2. //your code 

我们获取这个类的示例 最常用的方式就是使用这个类公有的构造器,每个类默认会有一个无参的构造器

  1. public class AnimalTest
  2.  
  3. public static void main(String [] args)
  4. Animal animal = new Animal(); 

  5.  

还有另外一种方法,也就是我们需要了解的静态工厂方法(static factorymethod),它是一个返回类实例的静态方法。那么我们就给Student类一个静态工厂方法

  1. public class Animal
  2. private static Animal s = new Animal(); 
  3. public static Animal getInstance()
  4. return animal; 


  1. public class AnimalTest
  2.  
  3. public static void main(String [] args)
  4. Animal animal = new Animal(); 
  5. Animal animal2 = Animal.getInstance(); 

  6.  

说明这里面的静态工厂方法并不是设计模式中的工厂模式

2 静态工厂方法 与 构造器 的对比

我们可以通过类的静态工厂方法来获得示例,而不是单独是构造器。

2.1 使用 静态工厂方法 的优点

2.1.1 静态工厂方法是有名称的

与构造器相比,静态工厂方法是有名称的。 如果构造器的参数不能够明确的描述返回的对象,我们使用有名称的静态工厂方法就是比较好的。我们可以通过名称的含义来理解这个静态工厂方法返回类的示例。
这种方法尤其在一种情况下很突出,当一个类只能有一个带有签名的构造器。我们都是通过修改两个构造器中的参数列表中的参数类型顺序。面对这样的API 使用起来是很麻烦的,由于静态工厂方法 是有名称的,所以不受这个限制。

2.1.2 不必每次调用都创建新的对象

使用静态的工厂方法,我们可以预先构造好类的实例,或者将写好的实例放入缓存中,进行重复的利用。
静态的工厂方法能够进行重复的调用而返回相同的对象,这可以帮助类控制那些时刻哪些实例应该存在。这种类型的类被称为 示例受控制的类(instance-controlled),写这样的类有几个原因:

  • 确保这个类是单例模式(Singleton) 或者不可实例化的

  • 可是使用 "==" 操作符来代替 equals(Object)方法,提高性能

  1. public class AnimalTest
  2. public static void main(String [] args)
  3. Animal animal = Animal.getInstance(); 
  4. Animal animal1 = Animal.getInstance(); 
  5. System.out.println(animal == animal1); 


2.1.3 可以返回类子类的对象 *

这个特性是非常灵活的,我们先试图想象三个组件 服务接口(用于用户获得实例)提供者注册API(服务器注册的实例)服务访问API(用于用户访问,获取) .其中还有一个可选的组件 服务提供者接口(用于创建实例)。这个就是基本的服务提供者框架,它也可以变化为 适配器(Adapter) 模式
说明:如果有web开发经验的可以把这个当作一个用户拿不同的数据访问 action ,而action 返回不同的视图。而第四个组件我们可以想象成一个Spring 的 base 实例化 action 对象。
这里提供一个完整的代码,希望大家能够自己拿去运行一下、拓展一下。

  1. /** 
  2. *服务访问API 
  3. */ 
  4. public interface IAnimal

  5.  
  6. /** 
  7. *服务提供者接口 
  8. */ 
  9. public interface IProvider
  10. IAnimal ianimal()

  11.  
  12. public class Animal
  13.  
  14. //构造方法私有化,客户端不能够使用构造器获得对象 
  15. private Animal(){} 
  16.  
  17. //根据名称放入相应的实例 
  18. private static final Map<String,IProvider> providers= new ConcurrentHashMap<String,IProvider>(); 
  19. public static final String DEFAULT_IPROVIDER_NAME = "<dfe>"
  20.  
  21. //用户注册实例的接口 提供者注册API 
  22. public static void registerDefaultProvider(IProvider p)
  23. registerProvider(DEFAULT_IPROVIDER_NAME,p); 

  24.  
  25. public static void registerProvider(String name,IProvider p)
  26. providers.put(name, p); 

  27.  
  28. //用户获得实例的接口 服务接口 
  29. public static IAnimal newInstance()
  30. return newInstance(DEFAULT_IPROVIDER_NAME); 

  31.  
  32. public static IAnimal newInstance(String name)
  33. IProvider p = providers.get(name); 
  34. if(p == null
  35. throw new IllegalArgumentException( 
  36. "没有实例注册这个名称:"+name); 
  37. return p.ianimal(); 

  38.  

  39.  
  40. public class Provider implements IProvider
  41.  
  42. private IAnimal iAnimal; 
  43.  
  44. public Provider(IAnimal iAnimal)
  45. this.iAnimal = iAnimal; 

  46.  
  47. @Override 
  48. public IAnimal ianimal()
  49. return iAnimal; 

  50.  

  51.  
  52. public class Person implements IAnimal
  53. @Override 
  54. public String toString()
  55. return "Person 实例"


  56.  
  57. public class Dog implements IAnimal
  58. @Override 
  59. public String toString()
  60. return "Dog 实例"


  61.  
  62. public class AnimalTest
  63. public static void main(String [] args)
  64. Animal.registerProvider("dog", new Provider(new Dog())); 
  65.  
  66. Animal.registerProvider("person", new Provider(new Person())); 
  67.  
  68. IAnimal dog = Animal.newInstance("dog"); 
  69. IAnimal person = Animal.newInstance("person"); 
  70.  
  71. System.out.println(dog.toString()); 
  72. System.out.println(person.toString()); 


  73.  

2.1.4 创建参数化类型实例更加简洁

在创建参数化类型实例的时候,静态工厂方法 会让代码变得更加的简洁
在调用参数化的构造器的时,即时类型参数很明显,也必须指明。通常要接连提供两次类型参数

Map<String,List<String>> m = new HashMap<String,List<String>>();

这个类型的声明太过于冗长,假设HashMap 有一个这样的静态工厂方法:

  1. public static <K, V> HashMap<K, V> newInstance(){ 
  2. 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 总结

静态工厂方法和公有构造器是各有用处的,但是我们需要理解他们的长处。静态工厂方法通常更加的合适,希望我们以后第一时间不是考虑构造器,而是想想静态工厂方法。

posted on 2016-05-13 15:37  ybbzbb  阅读(687)  评论(0编辑  收藏  举报