Effective java学习笔记一:考虑使用静态工厂方法代替构造方法

一个类允许客户端获取其实例的传统方法就是提供一个公共的构造方法。也就是通过new调用构造器。其实大多数情况下,更好的方法应该是自己写一个公共静态工厂方法,返回类的实例。比如下面这个例子。

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

提供公共静态工厂方法而不是公共构造方法有优点也有缺点。
首先我们来谈一下使用静态工厂方法的优点:

优点一:它不像构造方法,它是有名字的,这样就具有很强的区别性。

构造方法名只能使用类名,如果有多个构造器,程序员只能通过参数列表和参数类型的顺序来区别。这样可读性差,容易记错。
我们创建一个User实体类

public class User{
    private String nickName;
    private String realName;
    private Integer age;
    private String sex;
    //getter and setter 方法
}

我们来假设一个场景,根据nickName 和age来创建一个用户,使用构造器代码如下。

public User(String nickName, Integer age){
    this.nickName = nickName;
    this.age = age;
}

再假设我们需要根据realName和age来创建一个用户,使用构造器的参数类型,和上面那个相同(一个String类型,一个Integer类型),为了解决这个问题,我们可以调整参数顺序来解决这个问题。

public User(Integer age, String realName){
    this.age = age;
    this.realName = realName;
}

那如果我们再使用sex和age来创建用户,如果使用构造器的话,我们就没有办法了。
所以我们应该使用静态工厂方法来代替构造器。

public static User getByNickNameAndAge(String nickName, Integer age){
    User user = new User();
    user.setNickName(nickName);
    user.getAge(age);
    return user;
    }

public static User getByRealNameAndAge(String realName, Integer age){
    User user = new User();
    user.setRealName(realName);
    user.getAge(age);
    return user;
    }

public static User getBySexAndAge(String sex, Integer age){
    User user = new User();
    user.setSex(sex);
    user.getAge(age);
    return user;
    }

这样既解决了上面的问题,而且三个方法名称不同,并且暗示了使用的参数,能区别差异,不容易出错。

优点二:与构造方法不同,它们不需要每次调用都创建一个新对象。

使用静态工厂方法,在有些使用场景下,可以重复使用一个提前生成的对象,或者从缓存中获取一个对象,避免创建不必要的重复对象。文章开头的例子中的 Boolean.valueOf(boolean)方法,调用时就不用创建新的对象。这样节省了内存开销,也提高了性能。同时,和每次都 new 一个新的对象,都是不同的对象相比,在这种重复使用的场景中,每次返回的对象都是严格意义相同的对象,可以做到对象级别的控制。这种控制对单例和不可实例化的使用场景很有用,并且可以放心的使用 == 代替 equals 方法,以提高性能。比如枚举类就使用了这样的技术。

优点三:与构造方法不同,它们可以返回其返回类型的任何子类型的对象。

这为你在选择返回对象的类时提供了很大的灵活性。
构造函数只能返回该类的实例。而静态工厂方法可以返回该类的子类型。
从Java 8开始,接口不能包含静态方法的限制被取消了,所以在方法声明为接口类型时,使用静态工厂方法可以返回接口的某个类型的具体实现。但是,请注意,将这些静态方法的大部分实现代码放在单独的包私有类中仍然是必要的。 这是因为Java 8要求所有接口的静态成员都是公共的。

优点四:返回对象的类可以根据输入参数的不同而不同。 声明的返回类型的任何子类都是允许的。 返回对象的类也可以随每次发布而不同。

优点五:在编写包含该方法的类时,返回的对象的类不需要存在。

使用静态工厂方法也有缺点:

一:只提供静态工厂方法的主要限制是,没有公共或受保护构造方法的类不能被子类化。

二:程序员很难找到它们。

它们不像构造方法那样在API文档中突出,因此很难找出如何实例化一个提供静态工厂方法而不是构造方法的类。Javadoc工具可能有一天会引起对静态工厂方法的注意。与此同时,可以通过将注意力吸引到类或接口文档中的静态工厂以及遵守通用的命名约定来减少这个问题。下面是一些静态工厂方法的常用名称。以下清单并非完整:

  • from——A类型转换方法,它接受单个参数并返回此类型的相应实例,例如:Date d = Date.from(instant);
  • of——一个聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:Set faceCards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf——from和to更为详细的替代 方式,例如:BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance或getinstance——返回一个由其参数(如果有的话)描述的实例,但不能说它具有相同的值,例如:StackWalker luke = StackWalker.getInstance(options);
  • create 或 newInstance——与instance 或 getInstance类似,除了该方法保证每个调用返回一个新的实例,例如:Object newArray = Array.newInstance(classObject, arrayLen);
  • getType——与getInstance类似,但是如果在工厂方法中不同的类中使用。Type是工厂方法返回的对象类型,例如:FileStore fs = Files.getFileStore(path);
  • newType——与newInstance类似,但是如果在工厂方法中不同的类中使用。Type是工厂方法返回的对象类型,例如:BufferedReader br = Files.newBufferedReader(path);
  • type—— getType 和 newType简洁的替代方式,例如:List litany = Collections.list(legacyLitany);
总之,静态工厂方法和公共构造方法都有它们的用途,并且了解它们的相对优点是值得的。通常,静态工厂更可取,因此避免在没有考虑静态工厂的情况下提供公共构造方法

猜你喜欢

转载自blog.csdn.net/soda_lw/article/details/80067714
今日推荐