尽量使用静态工厂方法替换构造方法来实例对象


对于一个类而言,为了让别的类获取自身的一个实例,最常用的方法就是提供一个公有的构造器。还有一种方法,就是一个类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。这里所说的静态工厂方法与设计模式中的工厂方法模式不同。

类可以通过静态工厂方法来对外暴露实例对象,而不是通过构造器。提供静态工厂方法而不是公有的构造器,这样做具有几大优势。

静态工厂方法替换构造器的优势

  1. 静态工厂方法与构造器不同的第一大优势在于,它们可以有不同的名称
    如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂方法会更容易使用,产出的实例代码也更易于阅读。例如,下列的Person类的构造器Person(String name)返回的Person对象可能是男人也有可能是女人还有可能是不男不女等可能。

    public class Person {
        private String name;
        private String sex;
    
        public Person(String name) {
            this.name = name;
        }
    
        // get/set ...
    }
    

    如果使用名为Person.getManPerson的静态工厂方法来表示需要获得的实例为男人,getWoManPerson方法表示获得女人对象,显然更为清楚。

    public class Person {
        private String name;
        private String sex;
    
        private Person(String name) {
            this.name = name;
        }
    
        public static Person getManPerson(){
            Person person = new Person("张三");
            person.setSex("男");
            return person;
        }
    
        public static Person getWoManPerson(){
            Person person = new Person("张三");
            person.setSex("女");
            return person;
        }
    
        // get/set ....
    }
    

    测试调用过程:

    public class CommonTest {
    	    public static void main(String[] args) {
    	        //获取性别为男的人
    	        Person manPerson = Person.getManPerson();
    	        //获取性别为女的人
    	        Person woManPerson = Person.getWoManPerson();
    	
    	        System.out.println(manPerson.toString());
    	        System.out.println(woManPerson.toString());
    	
    	    }
    	}
    
  2. 静态工厂方法与构造器不同的第一大优势在于,不必每次调用它们的时候都创建一个新的对象
    静态工厂方法可以使一个类避免产生多个相同对象。即将第一产生的对象缓存起来,后续在调用静态工厂方法的时候在缓存中提取,进行重复利用,从而避免创建不必要的重复对象。以下实例表示了该如何使用静态工厂方法不返回重复的对象。
    静态工厂方法定义:

    public class Person {
        private String name;
        private String sex;
        private static final Map<String,Person> map = new HashMap(64);
    
        private Person(String name) {
            this.name = name;
        }
    
        public static Person getManPerson(){
            if(map.containsKey("男人")){
                System.out.println("我是从缓存中获取的对象");
                return map.get("男人");
            }
            Person person = new Person("张三");
            person.setSex("男");
            map.put("男人",person);
            System.out.println("我是从新创建的对象");
            return person;
        }
    
        // get/set ...
    }
    

    测试重复调用静态工厂方法最后返回的对象是否是重复的:

    public class CommonTest {
        public static void main(String[] args) {
            //获取性别为男的人
            Person manPerson1 = Person.getManPerson();
            Person manPerson2 = Person.getManPerson();
            Person manPerson3 = Person.getManPerson();
        }
    }
    
    Result:
    我是从新创建的对象
    我是从缓存中获取的对象
    我是从缓存中获取的对象
    

    从结果可以看出,静态工厂可以有效的防止多次调用不会去重复的创建对象,而使用已经创建好的对象。

  3. 静态工厂方法与构造器不同的第一大优势在于,它们可以返回对应的子类型的对象

    如下所示可以不返回Person类型对象,可以返回Person对象的子类型对象。

    public class Person {
        private String name;
        private String sex;
        private static final Map<String,ManPerson> map = new HashMap(64);
    
        protected Person(String name) {
            this.name = name;
        }
    
        public static ManPerson getManPerson(){
            if(map.containsKey("男人")){
                System.out.println("我是从缓存中获取的对象");
                return map.get("男人");
            }
            ManPerson person = new ManPerson("张三");
            person.setSex("男");
            map.put("男人",person);
            System.out.println("我是从新创建的对象");
            return person;
        }
       }
    
  4. 静态工厂方法与构造器不同的第一大优势在于,可以使用泛型类型,使代码变得更加简介
    JDK 1.5之前有时候调用构造器的时候参数个数很多,这样使用构造器新建对象的时候参数使用的就会使代码很长,越来越复杂,但是有了静态工厂方法,编译器就会替你找到类型参数。这被称作类型推导,如下所示:
    没有使用静态工厂方法的HashMap实例化的时候:

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

    假如HashMap使用了静态工厂方法来使编译器自行推导类型(虽然后面更高版本的JDK编译器不使用静态工厂也可以自行推导类型了。)

    public static<k,v> HashMap<k,v> newInstance(){
    	return new HashMap<k,v>();
    }
    

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

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

静态工厂方法替换构造器的缺点

  1. 静态工厂方法的主要缺点在于,类如果不包含公有的或受保护的构造器,就不能被子类化
  2. 静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别

结论

简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处。静态工厂方法通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。

参考文献

Effective Java 第二版 Joshua Bloch (第二章,第一条)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值