Effective Java - Item 1: Consider static factory methods instead of constructors
考虑使用静态工厂方法来替代构造方法, 这样的做的好处有四点.
1. 更好的表意
有的构造方法实际上有特殊的含义, 使用静态工厂方法能更好的表达出他的意思. 例如 BigInteger(int, int, Random) , 它返回一个可能是素数的 BigInteger. 使用工厂方法 BigInteger.probablePrime 可以更好的表达出他的意思
2. 无需每次创建新对象
在某些场景下, 我们无需每次都创建新的对象, 这样就可以使用静态工厂方法替代构造方法, 例如 Boolean.valueOf(boolean) , 这样可以提高性能. 另一方面, 使用这种方式可以做 实例控制 (instance-controlled). 我们可以将类的实例控制在只有唯一一个, 这样在判断相等时可以使用 '==' 而不是 equals 方法, 以提升性能. Enum 类型就是这样的.
3. 可以放回任意本类的子类型
构造方法是 void 方法, 他不能有返回值. 而静态工厂方法则可以用来返回任意本类的子类型.
一个灵活的程序 API 可以放回非 public 类对象, 通过隐藏这些类对象的实现来实现 基于接口的框架 (interface-based frameworks). 接口实现对象由静态工厂方法返回. 由于接口不能有静态方法, 所以一般来说 Type 接口的实际返回类是一个名为 Types 的工具类的静态方法提供的.
例如, Java 的 Collections 框架就是这样, 它内部的便捷方法都是静态工厂方法, 并且都放回了非 public 类的实例, 而客户端无需知道他的具体实现细节, 只需要关注返回的类型接口. 例如
public static final Set EMPTY_SET = new EmptySet<>();
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
其中 EmptySet 就是一个 private static class.
此外, 静态工厂方法还可以返回方法声明中返回类型的任意子类型. 例如 EnumSet 的实现, 它的构造方法是 default 作用域, 但是它有一个静态方法 noneOf(), 这个方法会根据底层 enum 的大小来决定返回的 EnumSet 类型.
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
这样做的好处是实现对客户端隐藏, 客户端只需要关注返回的是类型是 EnumSet. 如果以后发现 RegularEnumSet 在少元素的情况下没有性能优势了, 我们可以在静态方法中把它去掉. 亦或者在静态方法中加入其他 EnumSet 的子类型.
Service provider framework
此外, 静态工厂方法可以用来实现 service provider framework, 例如 JDBC 就是这种. Service provider framework 系统由多个 service provider 实现一个 service, 并且只有系统的 service 接口对用户可见, 将 service 的实现和使用与用户解耦.
Service provider framework 的必要组件有:
- Service 接口, 用于提供服务
- Provider 接口, 用于实现 Service
- Provider registration API, 用于系统注册 provider
- Service access API, 用于客户端获取 service 实例
一般来说 Service access API 不需要强制客户端指定 provider, 如果未指定 provider, 他可以使用缺省的 provider. Service access API 就是 service provider framework 典型的 "flexible static factory".
在 JDBC 中, Connection 充当 Service 接口; Driver 充当 Provider 接口; DriverManager.registerDriver 充当 Provider Registration API; DriverManager.getConnection 充当 Service Access API.
模板代码如下
// Service provider framework sketch
// Service interface
public interface Service {
... // Service-specific methods go here
} // Service provider interface
public interface Provider {
Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
private Services() { } // Prevents instantiation (Item 4)
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
// Provider registration API
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
// Service access API
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return p.newService();
}
}
4. 减少了泛型类构造方法的冗长程度
泛型类的构造方法看起来总是很冗长, 例如:
Map<String, List<String>> m =
new HashMap<String, List<String>>();
使用静态工厂方法, 则可以减少这些代码
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
在构造时可以变成这样
Map<String, List<String>> m = HashMap.newInstance();
相对的, 使用静态工厂方法也有坏处
1. 使用静态工厂方法生成的非 public protected 类无法被继承
例如在 Collections Framework 中的那些 private 类, 都无法被继承.
2. 静态工厂方法无法和其他静态方法区分开来
对于一个构造方法是私有的, 而只能通过静态工厂方法来获取实例的类, 我们无法将这个构造的静态工厂方法与其他静态类区分开来, 所以只能够 Javadoc 的注释来标记他们.
最后, 是一些常用的静态工厂方法名
- valueOf - 返回更参数值相同的类实例
- of - valueOf 的简写
- getInstance - 返回一个实例, 如果不存在则创建之
- newInstance - 类似于 getInstance, 只是每次都创建一个新实例
- getType - 类似于 getInstance, 只是返回的实例类型由参数 Type 指定
- newType - 类似于 newInstance, 只是返回的实例类型由参数 Type 指定
Effective Java - Item 1: Consider static factory methods instead of constructors的更多相关文章
- Effective Java P2 Item1 Consider static factory methods instead of constructors
获得一个类的实例的传统方法是公共的构造方法,还可以提供一个公共的静态工厂方法(一个返回值为该类实例的简单静态方法), 例如Boolean(boolean 的封装类) public static Boo ...
- Effective Java 01 Consider static factory methods instead of constructors
Advantage Unlike constructors, they have names. (BigInteger.probablePrime vs BigInteger(int, int, Ra ...
- 读Effective Java笔记之one:static Factory methods instead of Constructors (静态工厂方与构造器)
获取类的实例的方法有很多种,在这很多种方法中,它们各有优缺,各有特点.这里,只介绍2中方法 1.使用构造方法 public class Person { private String sex; /** ...
- 《Effective Java》读书笔记 - 2.创建和销毁对象
Chapter 2 Creating and Destroying Objects item 1:Consider static factory methods instead of construc ...
- Effective Java —— 用静态工厂方法代替构造器
本文参考 本篇文章参考自<Effective Java>第三版第一条"Consider static factory methods instead of constructor ...
- Effective Java Index
Hi guys, I am happy to tell you that I am moving to the open source world. And Java is the 1st langu ...
- Effective Java 第三版笔记(目录)
<Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时 ...
- Effective Java 目录
<Effective Java>目录摘抄. 我知道这看起来很糟糕.当下,自己缺少实际操作,只能暂时摘抄下目录.随着,实践的增多,慢慢填充更多的示例. Chapter 2 Creating ...
- 【Effective Java】阅读
Java写了很多年,很惭愧,直到最近才读了这本经典之作<Effective Java>,按自己的理解总结下,有些可能还不够深刻 一.Creating and Destroying Obje ...
随机推荐
- 024 关于spark中日志分析案例
1.四个需求 需求一:求contentsize的平均值.最小值.最大值 需求二:请各个不同返回值的出现的数据 ===> wordCount程序 需求三:获取访问次数超过N次的IP地址 需求四:获 ...
- Wireshark网络分析就这么简单
tcpdump抓包命令: root#tcpdump -I eth0 -s 80 -w /tmp/tcpdump.cap 注:其中80表示,只抓每个包的前80个字节. 抓包时就筛选自己需要的包: Wir ...
- 002.NFS相关配置项
一 配置文件(/etc/exports) 1.1 配置文件格式 <输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)] 二 输出目录 ...
- Xamarin iOS教程之申请付费开发者账号下载证书
Xamarin iOS教程之申请付费开发者账号下载证书 Xamarin iOS使用真机测试应用程序 在讲解iOS Simulator时,已经提到了虽然iOS Simulator可以模仿真实的设备,但是 ...
- JDBC之批处理
JDBC之批处理 现在有这么一个需求,要求把2000条记录插入表中,如果使用java代码来操作,我们可以使用Statement或者PreparedStatement来实现,通过循环来把SQL语句一条又 ...
- 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和. 当n=7共14种拆分方法: 7=1+1+1+1+1+1+1 7=1+1+1+1+1+2 7=1+1+1+1+3 7=1+1+1+2+2 ...
- hdu 4461 第37届ACM/ICPC杭州赛区I题
题意:给两个人一些棋子,每个棋子有其对应的power,若b没有或者c没有,或者二者都没有,那么他的total power就会减1,total power最少是1,求最后谁能赢 如果b或c出现的话,fl ...
- PS小技巧之完美抠图
具体详细步骤如下01.打开图片,ctrl+j复制一层得到图层1,点击红圈处新建图层2,放于图层1与背景层之间,填充你喜欢的颜色,作为检查效果和新的背景图层. 02.点击图层1,用“快速选择工具”大致做 ...
- 中国移动CMPP协议、联通SGIP协议、电信SMGP协议短信网关
移动cmpp协议 英文缩写:CMPP (China Mobile Peer to Peer) 中文名称:中国移动通信互联网短信网关接口协议 说明:为中国移动通信集团公司企业规范.规范中描述了中国移动短 ...
- Programming 2D Games 读书笔记(第二章)
本意还是想了解DirectX的,由于网上拿不到书的pdf文档,幸好有作者的源代码示例,想完整的看一下,基本的游戏需要的点. 下面直接以代码为例,仅用于帮助自身理解 http://www.progr ...