zoukankan      html  css  js  c++  java
  • 第1条:考虑采用静态工厂方法代替构造器

    第1条:考虑采用静态工厂方法代替构造器

    对类而言,为了让客户端获取它自身的一个实例,最常用的方法就是提供一个公有的构造器。还有一种应该被程序员重视的方法:类提供一个公有的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。下面是来自Boolean(基本类型boolean的包装类)的简单示例,这个方法将boolean基本类型值转换成了一个Boolean对象引用:

    1 public static Boolean valueOf(boolean b) {
    2     return b?Boolean.TRUE:Boolean.FALSE;
    3 }

    类可以通过静态工厂方法来提供它的客户端,而不是通过构造器。提供静态工厂方法而不是公有构造器,这样做有几大优势:

    静态工厂方法与构造器不同的第一大优势在于,它们有名称。问题:市场上存在三种类型的可乐,不含糖、不含脂和含脂含糖其余的参数一样,请设计一个类用于返回两种不同类型的可乐。

     1 public class Coca {
     2     private final int carbohydrate;
     3     private final int calories;
     4     private final int fat;
     5     private final int sugar;
     6     //没问题,返回含糖含脂的可乐
     7     public Coca(int carbohydrate, int calories, int fat, int sugar) {
     8         super();
     9         this.carbohydrate = carbohydrate;
    10         this.calories = calories;
    11         this.fat = fat;
    12         this.sugar = sugar;
    13     }
    14     //没问题,返回不含糖的可乐实例
    15     public Coca(int carbohydrate, int calories, int fat) {
    16         super();
    17         this.carbohydrate = carbohydrate;
    18         this.calories = calories;
    19         this.fat = fat;
    20         this.sugar = 0;
    21     }
    22     //预期希望的结果:返回不含脂的可乐实例
    23     //编译错误:Duplicate method Coca(int, int, int) in type Coca
    24     public Coca(int carbohydrate, int calories, int sugar) {
    25         super();
    26         this.carbohydrate = carbohydrate;
    27         this.calories = calories;
    28         this.sugar = sugar;
    29         this.fat = 0;
    30     }
    31 }

    从上面很明显可以看出,因为构造器区别在于参数类型、个数、顺序不同而已,上面的第三个和第二个构造方法并没有这些不同,因此无法区别才导致报错。这时候,我们幸好有静态工厂方法,我们可以通过使用简单的公共静态方法返回一个类的实例。

     1 public class Coca {
     2     private final int carbohydrate;
     3     private final int calories;
     4     private final int fat;
     5     private final int sugar;
     6     private Coca(int carbohydrate, int calories, int fat, int sugar) {
     7         this.carbohydrate = carbohydrate;
     8         this.calories = calories;
     9         this.fat = fat;
    10         this.sugar = sugar;
    11     }
    12     public static Coca includeAllCoca(int carbohydrate, int calories, int fat, int sugar) {
    13         return new Coca(carbohydrate,calories,fat,sugar);
    14     }
    15     public static Coca noFat(int carbohydrate, int calories, int sugar) {
    16         return new Coca(carbohydrate,calories,0,sugar);
    17     }
    18     public static Coca noSugar(int carbohydrate, int calories, int fat) {
    19         return new Coca(carbohydrate,calories,fat,0);
    20     }
    21 }

    总结:当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出它们之间的区别。

    静态工厂方法与构造器不同的第二大优势在于,不必每次在调用它们的时候都创建一个新的对象。

    1 public class Elvis {
    2     private static final Elvis INSTANCE = new Elvis();
    3     private Elvis() {};
    4     public static Elvis getInstance() {
    5         return INSTANCE;
    6     }
    7 }

    静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻哪几个实例应该存在,这种类被称为实例受控的类(instance-controlled)。编写实例受控的类有几个原因。

    一:实例受控能保证它是一个Singleton或者是不可实例化的。

    二:它还使得不可变的类可以确保不会存在两个相等的实例,即当且仅当a==b的时候才有a.equals(b)为true,如果能保证这一点,客户端就可以使用==来代替equals(Object)方法,这样可以提升性能。

    静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型对象。这样我们在选择返回对象的类的时候就有了更大的灵活性。

     1 public class Person {
     2     public String name;
     3     public static Person getInstance() {
     4         return new Cooker();        //此处可返回new Cooker()或者new Player()
     5     }
     6 }
     7 
     8 class Player extends Person{
     9     
    10 }
    11 
    12 class Cooker extends Person{
    13     private String food;
    14     public Cooker setName(String name) {
    15         this.name = name;
    16         return this;
    17     }
    18     public Cooker setFood(String food) {
    19         this.food = food;
    20         return this;
    21     }
    22     public void cook() {
    23         System.out.println(name+"正在烹饪"+food+",请稍后...");
    24     }
    25 }

    客户端代码:

    1 Cooker cooker = (Cooker) Person.getInstance();
    2 cooker.setName("XXX").setFood("红烧狮子头").cook();

    运行结果:

    XXX正在烹饪红烧狮子头,请稍后...

    静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。遗憾的是,在调用参数化类的构造器时,即使类型参数很明显,也必须指明。这通常要求你接连两次提供类型参数:

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

    随着类型参数变得越来越长,越来越复杂,这一冗长的说明也很快变得痛苦起来。但是有了静态工厂方法,编译器就可以替你找到类型参数。这被称作类型推导(type inference)。例如,假设HashMap提供了这个静态工厂:

    1 public static <K,V> HashMap<K,V> newInstance(){
    2     return new HashMap<K,V>();
    3 }

    你就可以用下面这句简洁的代码代替上面这段繁琐的声明:

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

    静态工厂方法的主要缺点在于,类如果不含公共的或者受保护的构造器,就不能被子类化。例如静态工厂方法的优势二中的例子,该类无法被子类化。

    静态工厂方法的第二个缺点在于,它们与其它的静态方法实际上没有任何区别。在API文档中,它们没有像构造器那样在API文档中明确标识出来,因此,对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类,这是非常困难的。

    下面是静态工厂方法的一些惯用名称:

    valueOf-----不太严格地讲,该方法返回的实例与它的参数具有相同的值,这样的静态工厂方法实际上是类型转换的方法。

    of-----valueOf的一种更为简洁的替代。

    getInstance-----返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对于Singleton来说,该方法没有参数,并返回唯一的实例。

    newInstance-----像getInstance一样,但是newInstance能够确保返回的每个实例都与所有其它的实例不同。

    getType-----像getInstance一样,但是在工厂方法处于不同的类中的时候使用,Type表示工厂方法所返回的对象类型。

    newType-----像getInstance一样,但是在工厂方法处于不同的类中的时候使用,Type表示工厂方法所返回的对象类型。

  • 相关阅读:
    3.STM32复位系统
    3.CM3内核架构-寄存器
    2.STM32启动文件
    java线程池
    java多线程
    动态规划(dynamic programming)(二、最优子问题与重叠子问题,以及与贪心的区别)
    SOAP协议
    动态规划(dynamic programming)(一、简介,举例)
    红黑树-RBT(二、基本操作之插入)
    红黑树-RBT(二、基本操作之左旋)
  • 原文地址:https://www.cnblogs.com/remote/p/10088441.html
Copyright © 2011-2022 走看看