考虑使用静态工厂方法来替代构造方法, 这样的做的好处有四点.

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);elsereturn 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 accesspublic class Services {private Services() { }  // Prevents instantiation (Item 4)// Maps service names to servicesprivate static final Map<String, Provider> providers =new ConcurrentHashMap<String, Provider>();public static final String DEFAULT_PROVIDER_NAME = "<def>";
       // Provider registration APIpublic static void registerDefaultProvider(Provider p) {registerProvider(DEFAULT_PROVIDER_NAME, p);}       public static void registerProvider(String name, Provider p){providers.put(name, p);}// Service access APIpublic 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相关推荐

  1. Consider static factory methods instead of constructor

    抄袭自<Effective Java ,Second Edition> The normal way for a class to allow a client to obtain an ...

  2. 5天带你读完《Effective Java》(一)

    <Effective Java>是Java开发领域无可争议的经典之作,连Java之父James Gosling都说:"如果说我需要一本Java编程的书,那就是它了".它 ...

  3. 【Effective Java】第二章:静态工厂、构建器、强化Singleton属性、私有构造器、

    文章目录 一. 用静态工厂方法代替构造器 优势: 劣势: 实例代码: 二. 遇到多个构造器参数时要考虑使用构建器 ① 重叠构建器 ② JavaBeans模式 ③ Builder模式 三. 用私有构造器 ...

  4. Effective Java 学习笔记 1

    Item 1: Consider static factory methods instead of constructors  (多考虑使用静态工厂方法而不是构造方法) 使用静态工厂方法有以下几点好 ...

  5. Effective Java 2.0_中英文对照_Item 5

    文章作者:Tyan 博客:noahsnail.com | CSDN | 简书 Item 5: Avoid creating unnecessary objects It is often approp ...

  6. 《Effective Java》读书笔记 Item 1:考虑静态工厂方法,而不是构造器

    众所周知,要想能获取一个类的实例,该类得要提供一个public的构造器.但是<Effective Java>书中说还有一个方法,那就是提供静态工厂方法(static factory met ...

  7. Effective Java 读书笔记(一)

    前言: 开个新的坑位,<effective java>的读书笔记,之后有时间会陆陆续续的更新,读这本书真的感触满多,item01和item02就已经在公司的项目代码中看到过了.今天这篇主要 ...

  8. 《Effective Java》读书笔记 - 5.泛型

    Chapter 5 Generics Item 23: Don't use raw types in new code 虽然你可以把一个List<String>传给一个List类型(raw ...

  9. \(^_^)/ Effective java

    读<Effect Java中文版> 译者序 序 前言 第1章引言 1   第2章创建和销毁对象 4 第1条:考虑用静态工厂方法代替构造函数 4 第2条:使用私有构造函数强化singleto ...

最新文章

  1. linux shell 实现循环输出
  2. python Class:获取对象类型
  3. 数据库技巧——MySQL十大优化技巧
  4. 在jQuery中删除事件处理程序的最佳方法?
  5. [转]对于非数据库字段的查询过滤以及app_query.append的用法
  6. git 怎么读_python3中开源代码怎么读?
  7. Web前端工程师的一些常见误区介绍!
  8. PLSQL连接远程Oracle出现ORA-12541: 无监听程序
  9. (第十二章)创建数据表索引
  10. 2018年股票操作策略记录(1)
  11. 剑指offer——面试题54:表示数值的字符串
  12. js 原型链的介绍
  13. 数据分析2 - 基础篇
  14. lordPE 破除只显示60个进程限制 win10下可运行
  15. 使虚拟光驱DaemonTool在电脑开机时不自动启动
  16. 大数据时代,数据分析师的职业发展规划
  17. debian中修改freeswitch的 sip默认端口,防盗打
  18. 内存取证-Volatility安装使用以及一些CTF比赛题目
  19. 【C++ Primer Plus】第6章 分支语句和逻辑运算符
  20. 学计算机是不是必须要买电脑,上大学了,你必须买一部电脑吗?

热门文章

  1. 服务器架设笔记——httpd插件支持mysql字符集选择
  2. Windows下通过Python 3.x的ctypes调用C接口
  3. 【EMC】电磁兼容性相关名词解释、基础知识
  4. 【Qt】一个使用QEventLoop时,遇到的教训
  5. 东北师大计算机考研报名人数,东北师范大学考研难吗?一般要什么水平才可以进入?...
  6. 调用jsapi缺少参数appid_【Python学习笔记】18、函数的参数关键字参数
  7. vim删除多行_Vim 可视化模式入门 | Linux 中国
  8. OpenCV-Java版学习(1.在IDEA中使用OpenCV)
  9. android默认exported_android:exported 属性详解
  10. 【基础知识】win10常用快捷键