• 静态工厂方法代替构造器


    静态工厂方法代替构造器

    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 总结

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

  • 相关阅读:
    十二月第二周学习进度条
    《UML大战需求分析》阅读笔记4
    十二月第一周学习进度条
    十一月第四周学习进度条
    十一月第三周学习进度条
    十一月第二周学习进度条
    制作验证码(转)
    阅读笔记04
    课堂作业-购书问题
    阅读笔记03
  • 原文地址:https://www.cnblogs.com/ybbzbb/p/5489617.html
Copyright © 2020-2023  润新知