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 ...
随机推荐
- 缓冲区(buffer)与缓存(cache) 缓冲:缓解冲击,缓存:临时存储
缓存与缓冲区 简要概述 缓存(cache):故名思意就是临时存储一下数据的存储器,其他设备可能等下还用的到数据.缓存区可以用来做缓冲区 缓冲区(Buffer):故名意思就是解决设备之间速度不匹配的问题 ...
- Centos6.x安装之后的9件事
Centos6.x安装之后的9件事 这些不是必须都做的,只不过是我个人的习惯,在此记录一下. 1.修改yum源到国内 CentOS系统更换软件安装源 备份你的原镜像文件,以免出错后可以恢复. mv / ...
- Qt:QCoreApplication
0.说明 QCoreApplication提供了有关当前运行程序的相关信息,当前程序应当是非GUI程序.对于GUI程序,应该用QGuiApplication,而对于采用了Qt Widget模块的程序, ...
- 进程&线程(一)——multiprocessing,threading
本节内容为①进程线程的基础知识:②在Python的实现方法: 学习总结自: 一文看懂Python多进程与多线程编程(工作学习面试必读) - 知乎 multiprocessing 官方文档 1.进程线程 ...
- pandas模块补充
数据分析模块pandas和matplotlib补充 面向百度式编程 面向百度式工作 遇到没有见过的知识点或者是相关知识点一定不要慌,结合百度和已知的知识点去学习 pandas模块补充 基于numpy构 ...
- Element-UI tree 组件 点击后高亮显示的样式修改(背景色)
感觉默认的高亮背景色颜色太浅,修改一下: .el-tree-node:focus > .el-tree-node__content { background-color: #ccc !impor ...
- TensorFlow入门示例教程
本部分的代码目前都是基于GitHub大佬非常详细的TensorFlow的教程上,首先给出链接: https://github.com/aymericdamien/TensorFlow-Examples ...
- laravel 使用Redis
1.非框架中使用redis实例? 就是new出redis对象然后连接,然后键值操作即可 <?php $redis = new redis(); $redis->connect('127.0 ...
- CF1117G题解
题意:区间建笛卡尔树,求每个节点的siz之和. 首先看到笛卡尔树,就应该想到,因为这是一个排列,可以找到通过左边和右边第一个比自己大的元素来"建立"笛卡尔树. 设 \(l(u)\) ...
- XML解析与文件存取
using System; using System.IO; using System.Text; using System.Xml; namespace XMLDemo { class Progra ...