- 首先,什么是静态方法?
静态方法是使用static关键字标记的方法,它属于类,不属于对象,意思就是,你在使用它的时候不需要创建对象,直接使用类名.方法就能使用,它的特点是在同一个类里,静态方法会被优先分配内存,如果这个方法会在多个地方调用,那么它也只分配了一次内存,而不像实例方法,需要创建多个对象,分配多块内存,所以它的适用场景在于,某个会被常用的公用方法,比如一些工具类的静态方法。 - 什么是静态工厂方法?
这里的“工厂”并不是指设计模式中的工厂模式,而只是用于命名一个生产某个类的对象的方法。顾名思义,静态工厂方法,指定的是一个静态的、用于生成某个类的对象的方法。
理清楚上面2个问题之后,再对照标题提问一下,为什么要使用静态工厂方法代替构造器呢?
在java中创建对象一般就是直接new一个,如果需要传参数,那么就直接在构造函数中定义即可,如果这个类的对象需要提供多种不同参数的创建形式,直接重载即可。
比如一个封装过的模板界面类UIActivity,它能支持设置返回键图标、设置标题、设置右上角button图案等功能,传入不同的参数,会生成不同的对象,生成不同界面,我们可以这样写它的构造函数:
...
public void UIActivity(String backIcon) {...}
public void UIActivity(String backIcon, String title) {...}
public void UIActivity(String backIcon, String title, String buttonIcon) {...}
...
这样写之后,使用者就可以根据需要选择构造器创建对象了。
这可能是平时开发中很常见的一些做法,甚至有见过一类里有10几种构造函数的。
这么做有什么问题吗?从编译和语法的角度上讲没有问题,但是在代码的可阅读性上是很有问题的,如果一个类构造函数的种类非常多,那么使用者在选择构造器的时候,单纯的通过传参的名称和数量去判断哪种构造器会产生哪种类型的对象是一件很累人的事情,尤其是当这个类的创建者和使用者不是同一个人的时候。
第二个问题,就上面这个例子,如果需要提供这样一种构造器呢:只需要设置返回键图标和右上角button的图标,不需要设置title,那么这里多定义一个构造器的话:
...
public void UIActivity(String backIcon) {...}
public void UIActivity(String backIcon, String title) {...}
public void UIActivity(String backIcon, String title, String buttonIcon) {...}
public void UIActivity(String backIcon, String buttonIcon) {...}
...
第二个构造器和第四个构造器就重复了,编译会不通过。
但是如果我们使用静态工厂方法来代替构造函数,就可以解决以上2个问题。比如:
...
//假设只带BackIcon的界面叫X界面,带BackIcon和title的叫Y界面,带BackIcon和右上角button的界面叫Z界面
public static UIActivity getXActivity(String backIcon) {...}
public static UIActivity getYActivity(String backIcon, String title) {...}
public static UIActivity getZActivity(String backIcon, String buttonIcon) {...}
...
在这种写法下,使用者不需要关注用什么参数获得什么样的实例,他只需要根据名字来决定用哪一个静态方法,而开发者也不会遇到上面提到的重复构造器的问题。
使用静态工厂方法还有哪些好处呢?
第一,和new关键字不同,它不一定要返回一个新的对象,它可以返回一个已经存在的对象,进而减少对象开销,或者实现某种业务的需要。最常见的用法就是单例模式了。
第二,和new关键字不同,它不一定只能返回这个类的对象,它在配合java特性中的多态时,可以返回它的子类的对象。这点可能有人会有疑惑点,就是它的子类自己有一个静态工厂方法不就行了吗?为什么要通过父类去创建并返回?主要有2个方面的原因,其一是如果这个类以及这个类的子类的开发者不希望用户可以直接使用子类的时候(这可以减少使用者的使用成本,使用者不用关心有多少子类以及子类的区别),也就是子类是非公共类的时候,静态工厂方法就能派上用场了;其二是可以符合面向接口编程的写法。
第三,可以选择性的实例化相关的类。比如说有1个静态工厂方法,会根据使用者的传参决定返回不同类的对象,该工厂方法会返回3中不同类的对象,当使用者传参指定使用第一种类的对象时,第二种和第三种类不需要实例化。
原文的说法:在编写包含该方法的类时,返回的对象的类不需要存在
使用静态工厂方法代替构造器也有2个缺点:
1.因为没有提供公有的或者protected的构造器,该类将不能被继承,这是硬伤(不要想着再提供一个构造器就行了嘛,构造器和静态工厂方法并存,如果真这样做,使用者往往在发现了有构造器之后,就不会去查看是否有静态工厂方法了,这就间接废弃了静态工厂方法)。
2.静态工厂方法的命名没有构造器的命名约束,也就是开发者可以随意命名,这在某种程度上也会让使用者不知所云,毕竟每个人的命名习惯都不同。
针对问题2,大家有了一些约定俗成的命名方式,你也会在SDK中经常看到这样的静态工厂方法命名:
- from - 传入单个参数进行类型转化
- of - 传入多个参数去生成一个对象
- valueOf - 传入的参数类型更加烦琐的情况下去生成一个对象
- instance/getInstance - 通过传参描述需要的对象,不一定返回新对象
- create/newInstance - 和上面的区别是确保会返回新的对象
- getXXX/newXXX - 其实只是把instance更加具体化一点,比如getDate/newDate
- XXX - 更加简洁的一种命名,就是把get和new都省略了,比如直接就命名为date()方法
本文探讨了静态工厂方法在Java中的应用,对比构造器,分析了其在代码可读性、对象开销和多态性上的优势,同时也讨论了其限制。
369

被折叠的 条评论
为什么被折叠?



