使用静态工厂方法而不是构造器

注意:静态工厂方法不是设计模式中的工厂方法。

一个类向客户端提供静态工厂方法有如下好处:

  • 有名称,不用根据参数类型和顺序区分重载方法,让代码更易读
  • 是否每次调用都需要新对象是可控制的,对于不可修改的对象可以采取缓存对象来提高性能,例如可以使用==来判断对象是否相等,而不使用equals,可以提高性能。
  • 工厂方法体内可返回返回类型的任何子类型,这在选择返回对象的类型上有很大的灵活性。一个对外隐蔽了实现类(即所返回的对象的类型对外是不可见的)的API是非常紧凑的API。这种技巧本身把自己引导成了基于接口的框架(接口为静态工厂方法提供返回类型)。接口不能有静态方法,因此按惯例返回类型为接口Type的静态工厂方法会放在一个不可实例化的类Types中:例如Java集合框架中的Collections有32个对集合接口的非常方便的实现,分别提供不可修改集合、同步集合等等,几乎所有这些实现都是通过一个不可实例化的类(java.util.Collections)的静态工厂方法导出,这些工厂所返回的类都是非public的。当前集合框架API比导出32个public类要更小,不单只在API的代码量上得到减少,同时还在概念的重量级方面。用户一眼就知道所返回的对象正如接口所描述的一样,不需要读取额外的类实现文档。另外,使用这样一种静态工厂方法需要客户端使用接口来引用这个工厂方法所返回的对象,而不是使用实现类引用,这是一个好的实践。不只由静态工厂返回的对象的类型可以是非public的,根据传入静态工厂的参数的不同,这个返回对象的类也是可变的,只要是静态工厂方法声明的返回值类型的子类型就都是允许的。静态工厂返回值类型也是可以在各发布版间变化的,以增强可维护性和性能。Java1.5引入的java.util.EnumSet没有public构造器,只有静态工厂,这些静态工厂根据底层枚举类型的大小返回两个实现中的一个:如果元素小于等于64,静态工厂返回一个RegularEnumSet实例,它由单个long支持; 如果枚举类型有大于64个元素,静态工厂会返回一个JumboEnumSet实例,它由一个long数组支持。这两个实现类对客户端是不可见的。

     由静态工厂方法返回的对象的类甚至不必在编写静态工厂方法所在的类时就存在。静态工厂的这种灵活性是服务提供者框架(service provider framework)的基石,例如JDBC。服务提供者框架是一个系统,在这个系统中,服务提供者实现服务,这个系统让这些实现对客户端可用,从而把客户端与实现分离。

     服务提供者框架有三个基本的组件:由提供者实现的服务接口、系统用于注册服务实现并让其对客户端可用的提供者注册API、客户端用于获取服务实现的服务访问API。服务访问API通常允许让客户端指定某些条件以便选择一个提供者,但客户端不是必须要指定,如果没有指定,API会返回一个默认实现。服务访问API就是构成服务提供者框架的基石的灵活静态工厂。

    服务提供者接口有一个可选的组件:服务提供者接口,由提供者用于创建他们自己的服务实现的实例。在没有服务提供者接口的情况下,实现是通过类名进行注册,并通过反射进行实例化。在JDBC中,Connection就是服务接口,DriverManager.registerDriver()就是服务提供者注册接口,DriverManager.getConnection()是服务访问接口,Driver是服务提供者接口。

    现在有许多服务提供者框架模式的变种,例如,通过使用适配器,服务访问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();
    }
}

  • 减少创建参数化类型实现时的冗余信息

没有使用静态工厂时:

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();

遗憾的是,标准的集合类实现中,例如HashMap并没有类似上面定义的工厂方法。但可以把这种方法放到自己的工具类中,更加重要的是,可以在自己的参数化类中提供这种静态工厂方法。

 

只提供静态工厂方法的类的主要缺点在于不能被子类化,因为没有public或protected的构造器的类是不能被子类化的。

由public权限的静态工厂返回的非public类也是不能被子类化的,例如,不能子类化Collections里面的实现类,这可以说是因祸得福,因此这样可以促进程序员使用组合而不是继承。

另一个缺点在于静态工厂方法不易与其它静态方法区分开。

主要是因为静态工厂方法不像构造器那样明显地出现在API文档中,因此很难知道如何使用类中提供的静态工厂来代替构造方法实例化对象。可以通过在类或接口中写注释来说明,并且让静态工厂方法名遵循约定:

valueOf、of、getInstance、newInstance、getType、newType

总之,静态工厂和构造方法各有优缺点,但一向在使用构造器之前优先考虑使用静态工厂。

转载于:https://www.cnblogs.com/mark-chan/p/5401666.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

极光推送指定用户推送_App用户都睡着了?是时候用推送和活动唤醒一波了!

想要运营好一款App&#xff0c;引流、留存、促活三大环节必不可少。引流解决了用户来的问题&#xff0c;留存解决了用户留下来的问题&#xff0c;而促活解决的是让一部分新注册用户以及许久没有动静的老用户&#xff0c;在平台中再次活跃起来。今天&#xff0c;我们就来聊聊关于…

扫描sdcard文件(递归)

private void saoMiaoSdCard() {// TODO Auto-generated method stub// 判断是否挂载if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 获取sdcardFile sdcard Environment.getExternalStorageDirectory();// 创建集合对象list_file new A…

oracle awr报告生成_分享AWR报告的生成和简单分析方法

生成AWR报告方法&#xff1a;第一步&#xff1a;数据库压力测试卡开始时&#xff1a;生成第一个快照&#xff1a;Sql>exec dbms_workload_repository.create_snapshot();第二步&#xff1a;数据库压力测试结束时&#xff1a;生成第二个快照Sql>exec dbms_workload_reposi…

java date.from_java datefromat

DateFormat 中的格式一致 即可) java.text.SimpleDateFormat sdf new java.text.SimpleDateFormat("M/dd/yyyy hh:mm:ss a",java); java.......SimpleDateFormat常用法_计算机软件及应用_IT/计算机_专业资料。有关javaSInpleDateFormat类的常用操作 1. SimpleDateFo…

selenium打开Firefox、IE、Chrome浏览器【python】

selenium打开不同浏览器的脚本。 1.Firefox from selenium import webdriverdriverwebdriver.Firefox() driver.get("http://www.baidu.com") 这里要注意打开的域名一定要加前http:// 否则会报错&#xff1a;selenium.common.exceptions.WebDriverException: Messag…

安卓开发 登录用户信息缓存_在Linux上使用finger命令查询登录用户信息

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号“智传网优”直接开始自助视频学习1. 前言本文主要讲解finger命令的作用和日常使用案例。finger命令是一个用户信息查询命令&#xff0c;它给出了所有登录用户的详细信息。…

java后端传object给js_【JSON】JSON在前端和后端传递

前后台最最传统的交互方式就是表单交互&#xff0c;然后用request.setAttribute方法设置结果&#xff0c;渲染jsp&#xff0c;然而随着前台界面的复杂程度的提高&#xff0c;或者是使用了某些前端框架(sigmagrid)越来越多的界面会使用异步方式提交数据。那么这个过程大致是什么…

POJ 1065 Wooden Sticks

http://blog.csdn.net/acdreamers/article/details/7626671 学习一下Dilworth定理 推荐一篇写得很好博客 要求最少的覆盖&#xff0c;按照Dilworth定理 最少链划分 最长反链长度 所以最少系统 最长导弹高度上升序列长度。 之前写的LIS模板不对。。。。。。 1 #include<cst…

python 对象_Python小课堂面向对象

Python3 面向对象Python从设计之初就已经是一门面向对象的语言&#xff0c;正因为如此&#xff0c;在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。如果你以前没有接触过面向对象的编程语言&#xff0c;那你可能需要先了解一些面向对象语言…

使用jemalloc优化java_Jemalloc优化MySQL和Nginx

Redis 2.4版本之后&#xff0c;默认使用jemalloc来做内存管理&#xff1b;tengine也整合jemalloc。jemalloc从各方评测的结果可见与google tcmalloc都不相伯仲&#xff0c;皆为内存管理器领域最高水平。如下图&#xff1a;最左边的就是glibc的malloc&#xff0c;最右边的就是je…

二维码的生成

我们目前用的是谷歌的zxing来生成二维码&#xff1b;下面呢我分别为大家介绍一下简单的二维码&#xff0c;中间有log的二维码&#xff0c;和彩色二维码&#xff1b; 需要两个权限&#xff1b; <uses-permission android:name"android.permission.CAMERA"/> <…

python读取python源代码文件_python 读写excel文件操作示例【附源码下载】

本文实例讲述了python 读写excel文件操作。分享给大家供大家参考&#xff0c;具体如下&#xff1a;对excel文件的操作&#xff0c;python有第三方的工具包支持,xlutils,在这个工具包中包含了xlrd,xlwt等工具包.利用这些工具&#xff0c;可以方便的对excel 进行操作。2. 安装,解…

Java工艺路线和工序_工序分散表现为工序多,工序内容( ),工艺路线长。

【其它】Baby-boomer parents seem to struggle with two things: saying no and letting go. (Para. 9)【填空题】若x5,y10,则x>y&&xy--的逻辑值是____________。【单选题】若a的值为3时,下面程序段被执行后,C的值是() c1 if(a>0) if(a>3) c2; else c3; else…

【代码升级】【iCore3 双核心板】例程二十八:FSMC实验——读写FPGA

实验指导书及代码包下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1i6WL51V 密码&#xff1a;1mk4 iCore3 购买链接&#xff1a; https://item.taobao.com/item.htm?id524229438677 转载于:https://www.cnblogs.com/xiaomagee/p/5409024.html

sqlserver连接字符串_【自学C#】|| 笔记 39 SQL server 连接数据库

一、ADO.NET数据库操作 任何一个应用程序都离不开数据的存储&#xff0c;数据可以在内存中存储&#xff0c;但只能在程序运行时存取&#xff0c;无法持久保存。数据还可以在磁盘中以文件的形式存储&#xff0c;但文件的管理和查找又十分烦琐无法胜任大数量的存储。将数据存储…

roll() java_Java Calendar roll()用法及代码示例

Calendar类中的roll(int calndr_field&#xff0c;boolean up_down)方法用于通过上下移动传递的字段单个时间单位来对传递的日历字段进行操作。这涉及在不更改较大字段的情况下对时间字段进行加法或减法。用法:public abstract void roll(int calndr_field, boolean up_down)参…

Android Studio、 补充知识以及主要组件

转载于:https://www.cnblogs.com/arxk/p/5410597.html

python如何全网爬取_如何通过Python爬取互联网

大家用过谷歌&#xff0c;百度吧。这类搜索引擎是怎么对外提供服务的呢&#xff1f;显然&#xff0c;这不是本文要说的事情。但是&#xff0c;任何一个搜索引擎&#xff0c;都缺不了网页收录这个步骤&#xff0c;所以网络爬虫是搜素引擎最重要&#xff0c;也是最基本的组成部分…

using(){},Close(),Dispose()的区别

//用Close(),Dispose()方式关闭连接 string connString "Data Source(local);Initial CatalogLinq;Integrated SecuritySSPI"; SqlConnection conn new SqlConnection(connString); conn.Open(); conn.Close(); conn.Dispose();//用using方式关闭连接 string connS…

python+selenium获取cookie session_Python Selenium模拟登录成功后,使用此cookie、利用requests库进行get时,提示“非法登陆”。...

一. 步骤概述a. 模拟登录学校选课系统(使用Selenium库登陆http://xk.suibe.edu.cn/xsxk/login.xk)b. 取得cookie后传入requests的session中。(参考博客&#xff1a;https://blog.csdn.net/big__v/article/details/78151940)c. 使用requests库中的post提交选课序号至http://xk.s…