技术公众号:Java In Mind(Java_In_Mind),欢迎关注!
前言
我相信大部分都看过《Effective Java》这本Java神书,这本书在我学习Java的路上给了我帮助可以说是受益匪浅,书中第一篇就建议:考虑用静态工厂方法代替构造器。我自从看了之后就开始使用实践该建议,到现在已经基本偏爱静态工厂方法,这里我就谈谈使用以来的一些领悟。
使用静态工厂方法的优缺点
优点
- 静态工厂方法有名称
- 不必每次调用都创建一个新对象
- 可以返回原类型的任何子类型的对象
- 创建参数化类型实例的时候,它们使代码更加简洁
缺点
- 类如果不含公有的或者受保护的构造器,就不能被子类化
- 静态工厂方法与其他静态方法没有任何区别
静态工厂方法有名称
这点确实是很好地优势,静态工厂方法有自己的名称,也就意味着可以表达某种意思,或者某些特殊的实例,例如,在使用一些通用返回结果的时候,可以用类似如此的静态工厂方法来创建实例:
public class Result {
private boolean success;
private String message;
private Object content;
private Result(boolean success,String message,Object content){
this.success = success;
this.message = message;
this.content = content;
}
public static Result ofSuccess(Object content){
return new Result(true,"处理成功",content);
}
public static Result ofFail(String message){
return new Result(false,message,null);
}
// getter and setter...
}
//使用
Result.ofSuccess(content);
Result.ofFail("处理失败,找不到该记录");
这样子,通过静态工厂方法就可以很好表达要构造的对象的目的,而调用方可以比较方便获取实例。
不必每次调用都创建一个新对象
这个其实很常见,如Boolean类中的几个valueOf
静态工厂方法,Boolean有两个静态域:TRUE
和FALSE
,这样就不用每次创建一个新对象:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
其实,我们在使用单例的时候,通常获取单例的方法就是一个静态工厂方法,并且也是不用创建一个新的对象:
public class SingleTon {
private static SingleTon singleTon = new SingleTon();
private SingleTon() {
}
public static SingleTon getInstance() {
return singleTon;
}
}
可以返回原类型的任何子类型的对象
静态工厂方法返回的实例可以是该类型的子类,而这个子类可以在编写静态工厂方法时不存在,这也就意味着可以由其他方来实现,也意味着对拓展友好,还可以根据静态工厂方法的入参来选择不同的实现,非常灵活。
-
在JDK中有个例子就是
EnumSet
,根据入参的枚举的枚举值量来决定使用哪种枚举实例。 -
在使用JDBC的时候,
DriveManager
在获取连接前得先加载对应的实现驱动,这样就能把实现交给第三方来完成,自己只需提供上层的抽象框架 -
日常编程中可以用于同一需求的不同实现,通过静态工厂方法选择实现,并且有利于后期拓展,例如事件处理:
public abstract class EventHandler { private static Map<String,EventHandler> handlers = new HashMap<>(); //事件类型 public abstract String type(); //子类注册入口 public static void register(EventHandler eh){ handlers.put(eh.type(),eh); } //获取不同的实现 public static EventHandler getHandler(Event e){ return handlers.get(e.getType()) } }
创建参数化类型实例的时候,它们使代码更加简洁
静态工厂方法还有一个好处就是提高代码的简洁度,也使得代码更加易读:
-
使用JDK的类型推断,不过这在现在的JDK已经可以直接推断了
public class Maps { public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>(); } } //使用静态工厂方法 Map<String,Object> map = Maps.newHashMap(); //使用构造方法 Map<String,Object> map = new HashMap<>();//JDK1.6之前不支持这种写法
-
正如第一点的例子,静态工厂方法可以有自己的名称,可以有一些默认值设置,可以减少构建实例的参数,自然也会提高代码的简洁度
类如果不含公有的或者受保护的构造器,就不能被子类化
类不含有public或者protect的构造方法时,子类的构建需要使用到父类的构造器,这个时候静态工厂方法就不适合
静态工厂方法与其他静态方法没有任何区别
使用静态工厂方法的时候,如果该类包含其他的静态方法,那么这个时候对于开发编程是不友好的,特别是在方法比较多,命名比较混乱的时候,使用者不能一眼就知道该用哪个方法来构造自己想要的实例,所以在编写静态工厂方法的时候尽量使用大家习惯的、约定俗成的命名风格:
- of
- valueOf
- getInstance / newInstance
- getType / newType,其中Type为类型,如,EventHandler.getEventHandler()
总结
结合我个人的经验,我认为使用静态工厂方法的利大于弊:
- 提高代码的可阅读性
- 更加符合人类的正常思维方式,想要某个类的实例直接找某个类就能获取到,而不是通过new关键字
- 能够根据不同的需求创建不同的实例,而不用让看代码的人推测new出来的对象然后set某些值去猜测某种意图,通常我们的做法是注释,但是使用静态工厂的方法命名不是更加简洁吗
- 提高代码的拓展性 ,方便拓展,对修改友好,符合开闭原则
网友评论