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 ...
随机推荐
- bool 类型位逻辑运算符(| & ^)与条件逻辑运算符(|| &&)的区别
bool 类型的运算符 & ^| && || ! 运算符&&和|| 是短路的,这意味着如果左侧表达式的值足以确定结果,则它们将不会评估其右侧表达式. ...
- 使用lrzsz在windows、Linux之间互传文件
使用xshell自带的传输太慢 使用lrzsz进行文件互传: xshell远程linux 安装工具:yum install -y lrzsz,检查是否安装成功:rpm -qa |grep lrzsz ...
- Python:numpy
学习自:NumPy 教程 | 菜鸟教程 官网:Numpy官方文档 1.简介 numpy主要用于数组计算,包含: 1)一个强大的N维数组对象ndarray 2)广播功能函数 3)整合 C/C++/For ...
- WPF-ListView单元格设置文字换行
第2-6行 1 <ListView Name="HumidifyEventLog" Style="{StaticResource ListViewStyle}&qu ...
- Python 细聊可以媲美 PS 的 PIL 图片处理库
1 . 前言 PIL 是 Python Image Library 的简称. PIL 库中提供了诸多用来处理图片的模块,可以对图片做类似于 PS(Photoshop) 的编辑.比如:改变图像大小.旋转 ...
- 『德不孤』Pytest框架 — 10、setUp()和tearDown()函数
目录 1.setUp()和tearDown()函数介绍 2.setUp()和tearDown()函数作用 3.setUp()和tearDown()函数说明 4.示例 (1)方法级 (2)类级 (3)函 ...
- python之random.seed()函数
Python--random.seed()用法 第一次接触random.seed(),可能理解的不是特别对,大家欢迎指错,整理自网络,侵权删除 概念 seed()是改变随机数生成器的种子,可以在调用其 ...
- HarmonyOS小白入门设备开发的“芯”路历程
HarmonyOS Connect设备开发,相信不少刚入门的开发者都被这些问题所困扰,面对五花八门的开发板不知道该怎么选取?芯片.模组.开发板傻傻分不清?如何使用代码控制开发板? 本期,我们将一一为你 ...
- docker学习(三) - docker理解及命令
Docker 包括三个基本概念 镜像(Image) 容器(Container) 仓库(Repository) 镜像 Docker 镜像就是一个只读的模板. 例如:一个镜像可以包含一个完整的 ubunt ...
- jdbc model 代码示例
package com.gylhaut.model; import java.util.Date; public class Goddess { @Override public String toS ...