如何书写一手优雅的代码之1:考虑用静态工厂方法代替构造器(EffectiveJava)

      lz敲代码这些年其实还没有思考过这个问题,其实说白了,之前的做法无非就是创建一个公有的构造器,当需要时便new,代码如下:

public class Role01 {
    private String name;
    private Integer age;
    private String address;
    private String car;
    //空的无参构造......getter、setter省略
    public Role01(){}

    //这个构造器是专门生成流浪汉对象
    public Role01(String name,Integer age){
        this.name=name;
        this.age=age;
        this.address="无家可归";
        this.car="无车族";
    }
    //这个构造器是专门生成普通人对象
    public Role01(String name,Integer age, String address){
        this.name=name;
        this.age=age;
        this.address=address;
        this.car="自行车";
    }
    //这个构造器是专门生成富豪对象
    public Role01(String name,Integer age, String address, String car){
        this.name=name;
        this.age=age;
        this.address=address;
        this.car=car;
    }
    //纯属为了打印好看,重写toString方法
    public String toString(){
        return "name:"+name+", age:"+age+", address:"+address+", car:"+car;
    }

    public static void main(String[] args){
        Role01 roles01 = new Role01("zhangsan",21);
        Role01 roles02 = new Role01("lisi", 22, "北京市朝阳区人民法院");
        Role01 roles03 = new Role01("wangwu", 23, "北京市朝阳区三里屯","兰博基尼");

        System.out.println(roles01);
        System.out.println(roles02);
        System.out.println(roles03);
    }
}

     上面这个就是平时大家用的比较多的方式。而这样也存在着一些问题。比如关于多个构造器之间仅仅参数不同或者参数类型顺序不同而造成的调用者的混淆,以及重复创建对象会耗费大量内存等等,下面我就具体列举4条静态工厂方法的优势供大家参考。

      1.静态工厂方法有名称,即方法名。这样便可以通过方法名来大体确定传参类型和个数,具体代码如下:

public class Role01 {
    private String name;
    private Integer age;
    private String address;
    private String car;
    //空的无参构造......getter、setter省略
    private Role01(){}
    //这个构造器是专门生成流浪汉对象
    private Role01(String name,Integer age){
        this.name=name;
        this.age=age;
        this.address="无家可归";
        this.car="无车族";
    }
    //这个构造器是专门生成普通人对象
    private Role01(String name,Integer age, String address){
        this.name=name;
        this.age=age;
        this.address=address;
        this.car="自行车";
    }
    //这个构造器是专门生成富豪对象
    private Role01(String name,Integer age, String address, String car){
        this.name=name;
        this.age=age;
        this.address=address;
        this.car=car;
    }

    public static Role01 GetPoolPerson(String name,Integer age){
        return new Role01(name, age);
    }
    public static Role01 GetOrdinaryPerson(String name,Integer age,String address){
        return new Role01(name, age, address);
    }
    public static Role01 GetRichPerson(String name,Integer age,String address,String car){
        return new Role01(name,age,address,car);
    }
    //纯属为了打印好看,重写toString方法
    public String toString(){
        return "name:"+name+", age:"+age+", address:"+address+", car:"+car;
    }
    public static void main(String[] args){
        Role01 roles01 = Role01.GetPoolPerson("zhangsan", 21);
        Role01 roles02 = Role01.GetOrdinaryPerson("lisi", 22, "北京市昌平区育新小区");
        Role01 roles03 = Role01.GetRichPerson("wangwu", 23, "北京市朝阳区三里屯", "兰博基尼");
        System.out.println(roles01);
        System.out.println(roles02);
        System.out.println(roles03);
    }
}
      原理:因为流浪汉对象没有住处,所以创建流浪汉对象时不需要传入住处,同理创建普通人也不必要传入跑车的参数(只是类别,目的是理解静态方法名所带来的便利)

打印结果相同,这种通过静态方法名称来创建对象方式最直接的特点就是一目了然,调用者仅通过方法名称就大致可以判断需要传的参数大大节省开发效率。
       2.不必要在每次调用他们的时候都创建一个新对象。这里就不得不用java.lang包下的一个Boolean类来举例子了。代码如下:

public class Role01 {
    public static Boolean valueOf(boolean b){
        return b ? Boolean.TRUE : Boolean.FALSE;
    }
    public static void main(String[] args){
        System.out.println(Role01.valueOf("a".equals(new String("a"))));
        System.out.println(Role01.valueOf(1==2));
        System.out.println("*****");
        System.out.println(Boolean.valueOf("aa").hashCode());
        System.out.println(Boolean.valueOf("aabb").hashCode());
        System.out.println(Boolean.valueOf(true).hashCode());
        System.out.println(Boolean.valueOf("true").hashCode());
    }
}

     上面true和false是我调用本类方法,而后面1237和1231是我调用Boolean工具类中的方法,那我到底想表达一个什么意思嘞?下面是Boolean类的源码...


我们可以看到对象只有两个地址值,一个是常量true->1231另一个就是false->1237。这样就极大地节省了重复创建对象所占用的内存。你可以尝试通过打印通过构造器创建的对象,每一次的地址值都是新的,说明对象是全新的。

     3.它们可以返回原返回类型的任何子类型的对象,这样我们在选择返回对象的类时就有了更大的灵活性。(下面摘自原文)

      这种灵活性主要在于,API可以返回对象,同时又不会使对象的类变成公有的,以这种方式隐藏实现类会使API变得非常简洁。适用于基于接口的框架。静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在,这种灵活的静态工厂方法构成了服务提供者框架的基础,例如JDBC API,服务提供者框架是指这样一个系统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。

      服务提供者框架中有三个重要的组件:服务接口,这是提供者实现的;提供者注册API,这是系统用来注册实现,让客户端访问它们的;服务访问API,是客户端用来获取服务的实例的。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件。如果没有这样的规定,API就会返回默认实现的一个实例。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。对于JDBC来说,Connection就是它的服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。

     4.静态工厂方法在创建参数化类型实例的时候,它们是代码变得更加简洁。

当用参数化类的构造器时,及时类型参数很明显,也必须指明。这通常要求你连接两次提供类型参数:

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

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

public static <K ,V> HashMap<K, V> newInstance(){

     return new HashMap<K, V>();

}

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

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

当然也有缺点:

1.类如果不含有公有的或者受保护的构造器,就不能被子类化。

这里简单复习一下访问权限

private:完全隐藏类的成员,这样,就不能从类的外边直接访问它们,我们提供set和get方法,保证类中数据域的安全。

default:指默认修饰符,什么都不加,实际上它限制的范围是一个包内可以访问,如果不在一个包内,即使继承关系仍然是不能访问的。

protected:经常需要允许子类访问定义在父类中的数据和方法,但是不允许非子类访问这些数据和方法,这种情况下就可以使用protected,它允许任何包中的子类访问父类。

public:这个就不多说了,大家都懂!

2.它们与其他的静态方法实际上没有任何区别

最后,我们在来提供一些惯用名称,供大家参考。

valueOf---不太严格地讲,该方法返回的实例与它的参数具有相同的值,类型转换方法

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

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

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

简而言之,静态工厂方法和构造器各自有各自的用处,切忌第一反应就是提供公有的构造器,而不考虑静态工厂。

第一篇文章终于写完了.................lz已经要阵亡了,遥望那遥不可及的终点,内心一凉..!!!大哭



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值