Effective Java —— 用静态工厂方法代替构造器
本文参考
本篇文章参考自《Effective Java》第三版第一条"Consider static factory methods instead of constructors"
另外参考了其它几篇文章的解读:
https://www.cnblogs.com/dyj-blog/p/8867028.html
https://blog.csdn.net/u014129886/article/details/89670049
前言
第一条的篇章来自"Creating and Destroying Objects",讲述的是何时并且如何创建对象,销毁对象,所以第一条的"静态工厂方法"不同于软件设计模式中的"工厂方法"(Factory Method Pattern),二者没有任何直接的关联
First Advantage —— they have names
构造方法的方法名始终与类名相同,当我们有不同的构造方法时,就需要保证不同的构造方法有不同的形参类型、形参个数以及形参顺序,以此实现构造方法的重载
原文将这种构造方式称为"a really bad idea",因为当构造方法增多时,显然我们难以判断每个构造方法将会构造怎样的实例,我们需要为每个构造方法书写清晰准确的代码注释,防止选择了错误的构造方法构造实例
但是静态工厂方法不同于构造方法,它的方法名不受类名的约束,我们可以根据返回的实例的具体用途或具体特征来设计方法名,原文举了一个BigInteger的一个例子如下:
For example, the constructor BigInteger(int, int, Random), which returns a BigInteger that is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime.
也因为静态工厂方法可以拥有自己独特的名字,所以我们可以在不同的静态工厂方法中调用相同方法签名的构造方法,不过为此构造方法传入了不同的实参
Second Advantage —— they are not required to create a new object each time they're invoked
从上面一个Advantage来看,我们需要在静态工厂方法内调用构造方法,实际上我们可以直接返回一个静态成员字段,这个字段就是这个类的实例
静态成员字段随类的装载便进行构建,也可以在第一次被用到时才进行实例化,这分别对应了单例模式下的饿汉模式和懒汉模式
当然我们也可以使用Map或List等数据结构来缓存实例化后的对象,当再次调用静态工厂方法时,可以直接从集合中进行获取
构造方法显然无法完成这一点,我们每次使用new关键字都会产生一个新的实例化对象
原文称这种静态工厂方法的解决方案为"instance-controlled"
Instance control allows a class to guarantee that it is a singleton or noninstantiable. Also, it allows an immutable value class to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern.(享元模式)
Third Advantage —— they can return an object of any subtype of their return type
构造方法只能返回本类的实例化对象,但是静态工厂方法能够返回本类的子类的实例化对象,提供更好的灵活性
我们还可以将子类的权限修饰符设置为private,从而精简API并隐藏更多的类实现信息
原文称这种静态工厂方法的解决方案为"interface-based framework"
One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks, where interfaces provide natural return types for static factory methods.
Fourth Advantage —— the class of the returned object can vary from call to call as a function of the input parameters
根据静态工厂方法的参数值不同,返回的对象的类也可以不同,这一条Advantage和上一条Advantage有一点相似之处, 如静态工厂方法所处的类有多个子类,那么在静态工厂方法内可以根据不同的条件返回不同的子类实例
我们用EnumSet抽象类作例,EnumSet抽象类有两个子类RegularEnumSet和JumboEnumSet,后者支持存储超过64个元素,前者只能存储64个以内,在EnumSet的noneOf()方法中,会根据Enum长度的不同,返回不同的子类
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);
}
Fifth Advantage —— the class of the returned object need not exist when the class containing the method is written
方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
原文称这种静态工厂方法的解决方案为"service provider frameworks",它有如下四个组件:
- service interface -> represents an implementation of service
- service provider interface (optional) -> describes a factory object that produce instances of the service interface
- service access API -> clients use it to obtain instances of the service
- provider registration API -> providers use it to register implementations (service interface or service provider interface)
若用Jdbc作例,上述四个组件对应如下:
- service interface -> interface Connection
- service provider interface -> interface Driver
- service access API -> DriverManager.getConnection()
- provider registration API -> DriverManager.registerDriver()
他们的作用分别为:
- Connection接口具备连接数据库的服务
- Driver接口的connect()方法返回Connection服务接口实例,是对Connection接口的又一层封装
- 用户使用DriverManager.getConnection()方法获取连接数据库的服务
- 服务提供者使用DriverManager.registerDriver()注册Driver实例
注意,从Java 6开始,Java提供了ServiceLoader服务提供者框架,不需要我们自行实现上面四个组件
Since Java 6, the platform includes a general-purpose service provider framework, java.util.ServiceLoader, so you needn't, and generally shouldn't, write your own (Item 59). JDBC doesn't use ServiceLoader, as the former predates the latter.
First Disadvantage —— classes without public or protected constructors cannot be subclassed
因为实例通过静态工厂方法返回,构造方法的权限修饰符通常设置为private,所以该类不可被继承
我们可以通过复合来解决这个缺陷
Arguably this can be a blessing in disguise because it encourages programmers to use composition instead of inheritance, and is required for immutable types.
Second Disadvantage —— they are hard for programmers to find
在Java doc中,构造方法的描述在比较靠前的位置,因此比较容易查阅,但是静态工厂方法的描述不仅比较靠后,而且和普通的静态方法混杂在一起,导致我们难以区分
我们可以通过统一命名规范来解决这个问题
- from() -> 类型转换方法,它只有单个参数,返回该类型的一个相对应的实例
- of() -> 聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来
- valueOf() -> 比 from 和 of 更烦琐的一种替代方法
- instance() 或者 getInstance() -> 返回的实例是通过方法的(如有)参数来描述,但是不能说与参数具有同样的值
- create()或者 newInstance() -> 像instance 或者 getInstance 一样,但 create 或者 newInstance 能够确保每次调用都返回一个新的实例
- getType() -> 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型
- newType() -> 像newInstance 一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型
- type() -> getType 和 newType 的简化版
Effective Java —— 用静态工厂方法代替构造器的更多相关文章
- Java - 用静态工厂方法代替构造器
Effective Item - 考虑用静态工厂方法代替构造器我们有两种常见的方法获得一个类的实例: 公有的构造器 提供静态工厂方法(static factory method) 相对公有的构造器,静 ...
- Effective Java 读书笔记(一):使用静态工厂方法代替构造器
这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...
- effective java 3th item1:考虑静态工厂方法代替构造器
传统的方式获取一个类的实例,是通过提供一个 public 构造器.这里有技巧,每一个程序员应该记住.一个类可以对外提供一个 public 的 静态工厂方法 ,该方法只是一个朴素的静态方法,不需要有太多 ...
- 【Effective Java读书笔记】创建和销毁对象(一):考虑使用静态工厂方法代替构造器
类可以提供一个静态方法,返回类的一个静态实例,如Boolean包装类的一个获取实例的静态方法 public static Boolean valueOf(boolean b) { return (b ...
- Effective java读书札记第一条之 考虑用静态工厂方法取代构造器
对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...
- 【读书笔记 - Effective Java】01. 考虑用静态工厂方法代替构造器
获取类的实例有两种方法: 1. 提供一个公有的构造器(最常用). 2. 提供一个公有的静态工厂方法(static factory method). // 静态工厂方法示例 public static ...
- 改善JAVA代码01:考虑静态工厂方法代替构造器
前言 系列文章:[传送门] 每次开始新的一本书,我都会很开心.新书新心情. 正文 静态工厂方法代替构造器 说起这个,好多可以念叨的.做了一年多的项目,慢慢也有感触. 说起构造器 大家很明白,构造器 ...
- 高效JAVA之用静态工厂方法代替构造器
程序员这行干的久了,总会染上一些恶习,我就染上一个让人深恶痛绝,自己却津津乐道的习惯,还不想改的那种,它可以叫做强迫症,也可以叫做洁癖.那就是我不允许我的IDEA出现一点点警告,什么黄色背景,绿色波浪 ...
- Effective Java 之-----静态工厂与构造器
一. 考虑用静态工厂方法代替构造器: 1)静态工厂方法与构造器不同的第一大优势在于:他们有名称.当一个类需要多个带有相同签名的构造器时,就用静态方法代替构造器,并慎重的选择名称以突出他们间的区别: 2 ...
随机推荐
- 一个简单的性能计数器:CodeTimer
public static class CodeTimer { public static void Initialize() { Process.GetCurrentProcess().Priori ...
- Python 小数据池和代码块缓存机制
前言 本文除"总结"外,其余均为认识过程:3.7.5: 总结: 如果在同一代码块下,则采用同一代码块下的缓存机制: 如果是不同代码块,则采用小数据池的驻留机制: 需要注意的是,交互 ...
- WPS:想让一个新标题后总跟着一种特定样式的文字
只需在这个后续段落样式中修改为你想要的那个样式即可
- Python数据可视化 -- Wordcloud
Python数据可视化 -- Wordcloud 安装 启动命令行,输入:pip install wordcloud word cloud 库介绍 及简单使用 wordcloud库,可以说是pytho ...
- 在矩池云上复现 CVPR 2018 LearningToCompare_FSL 环境
这是 CVPR 2018 的一篇少样本学习论文:Learning to Compare: Relation Network for Few-Shot Learning 源码地址:https://git ...
- 矩池云助力科研算力免费上"云",让 AI 教学简单起来
矩池云是一个专业的国内深度学习云平台,拥有着良好的深度学习云端训练体验,和高性价比的GPU集群资源.而且对同学们比较友好,会经常做一些大折扣的活动,最近双十一,全场所有的RTX 2070.Platin ...
- Mysql引擎、隔离机制、存储结构、索引
目录 数据库常用的两种引擎 两种引擎差异对比 如何选择引擎 两个引擎索引结构 查找mysql数据存储位置方式 MyISAM InnoDB 1. 非独立表空间 2. 独立表空间 3. idb文件存的哪些 ...
- Kendo UI Grid 使用总结
Kendo UI Grid控件的功能强大,这里将常用的一些功能总结一下. Kendo UI Grid 固定列 在使用Gird控件显示数据时,如果数据列过多,会出现横向滚动条,很多情况下,我们希望某些列 ...
- Excel文件导入SQL Server数据库
Excel表格的使用可谓是非常广泛,博主也简单百度了一下Excel的发展. 发展历程: 1982年 Microsoft推出了它的第一款电子制表软件-Multiplan,并在CP/M系统上大 Excel ...
- OpenCV基础_二
阈值和平滑处理 cv2.threshold()二值化函数 ret,thresh = cv2.threshold(src, thresh, maxval, type[, dst]) 参数 src:所要进 ...