Effective Java第二版笔记之考虑使用静态工厂方法代替构造器

创建对象最直接的想法就是通过new调用构造器,其实大多数情况下应当通过自己写一个静态公有方法,返回类的实例,比如下面这个方法:

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

和直接使用构造器相比,使用静态工厂方法具有以下优点:

  • 静态工厂方法可以根据用途自己定义名称(不必与类相同),可读性更强
  • 静态工厂方法可能不用在每次调用时都创建新对象
  • 静态工厂方法可以返回声明类型的子类型的实例

下面分别阐述这三个优点。

静态工厂方法可以根据用途自己定义名称

构造器的方法名只能使用类名,如果有多个构造器,只能通过参数类型甚至顺序来区分,这样可读性非常差,而且不容易记,调用的时候很容易出错。

public class TantanitReader {
    private int age;
    private String loginName;
    private String realName;
    private String career;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public String getCareer() {
        return career;
    }

    public void setCareer(String career) {
        this.career = career;
    }
}

比如在上面的例子中,类TantanitReader表示我的官方博客tantanit.com的读者信息,其中age表示年龄,loginName表示登录名,realName表示真实姓名,career表示职业,除了age是int类型之外,其它几个都是字符串类型。

假设现在需要根据年龄和登录名,创建一个读者,使用构造器的话,代码如下:

public TantanitReader(int age, String loginName) {
    this.age = age;
    this.loginName = loginName;
}

假设又有一个场景,需要根据年龄(int类型)和真实姓名(String类型)创建读者,如果使用构造器的话,参数类型和上一个构造器相同,要解决这个问题呢?机智如你,一定想到通过对调参数顺序这个取巧(而不优雅)的方法,来规避这个问题。

public TantanitReader(String realName, int age) {
    this.realName = realName;
    this.age = age;
}

如果一个类要通过构造器创建读者,就需要根据这两个构造器的参数顺序记住各自的用法,是不是很容易记错啊。就算记住了也要查文档或者看看TantanitReader这个类的源文件,再确认一遍吧。

好了,又来了一个场景,需要根据年龄(int类型)和职业创建读者,如果使用构造器的话,相信聪明如你,也是没有办法了。

所以,应当使用静态工厂方法来代替构造器。下面的代码很好地解决了这个问题:

public static TantanitReader getByAgeAndLoginName(int age,String loginName){
    TantanitReader tantanitReader=new TantanitReader();
    tantanitReader.setAge(age);
    tantanitReader.setLoginName(loginName);
    return tantanitReader;
}

public static TantanitReader getByAgeAndRealName(int age,String realName){
    TantanitReader tantanitReader=new TantanitReader();
    tantanitReader.setAge(age);
    tantanitReader.setRealName(realName);
    return tantanitReader;
}

public static TantanitReader getByAgeAndCareer(int age,String career){
    TantanitReader tantanitReader=new TantanitReader();
    tantanitReader.setAge(age);
    tantanitReader.setCareer(career);
    return tantanitReader;
}

三个方法使用不同的名称,并且在名称中暗示了参数的顺序,这样,在调用的时候,就不容易出错,也不用再查看文档或构造器的源码了。

静态工厂方法可能不用在每次调用时都创建新对象

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

静态工厂方法可以返回声明类型的子类型的实例

构造函数,只能返回该类的实例,不能返回该类的子类的实例,静态工厂方法不受这个限制,因此可以很好地使用Java语言的多态性。在方法声明返回值类型为父类型,甚至接口类型,而返回子类型或接口的某个类型的具体实现。这样,将来想返回其它子类型或接口的其它实现时,只要直接修改方法体,不用改方法的声明,从而不会影响调用方的使用。

小结

本文使用谈谈IT读者的例子介绍了使用静态工厂方法替代构造器的三个优点,在有些场景,这些优点会很明显,下次遇到了,记得考虑使用静态工厂方法哦!

欢迎扫描下方二维码关注微信公众号【谈谈IT】,第一时间获取最新文章。
欢迎关注同名公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值