静态工厂方法与传统构造方法

之前,我已经讨论过一些关于Builder模式的信息 , Builder Pattern是一种有用的模式,用于实例化具有多个(可能是可选的)属性的类,这些属性可以使读取,编写和维护客户端代码更加容易,还有其他好处。 今天,我将继续探索对象创建技术,但是这次是更一般的情况。

以下面的示例为例,除了说明我的观点外,它绝不是有用的类。 顾名思义,我们有一个RandomIntGenerator类,它生成随机的int数。 就像是:

public class RandomIntGenerator {private final int min;private final int max;public int next() {...}
}

我们的生成器采用最小值和最大值,然后生成这两个值之间的随机数。 注意,这两个属性都声明为final,因此我们必须在它们的声明或类构造函数中对其进行初始化。 让我们来看一下构造函数:

public RandomIntGenerator(int min, int max) {this.min = min;this.max = max;}

现在,我们还希望给客户提供仅指定最小值的可能性,然后为该整数生成介于最小值和最大值之间的随机值。 因此,我们添加了第二个构造函数:

public RandomIntGenerator(int min) {this.min = min;this.max = Integer.MAX_VALUE;}

到目前为止一切顺利,对吗? 但是,就像我们提供一个仅指定最小值的构造函数一样,我们只想为最大值做同样的事情。 我们将只添加第三个构造函数,例如:

public RandomIntGenerator(int max) {this.min = Integer.MIN_VALUE;this.max = max;}

如果尝试这样做,则会遇到以下编译错误: 类型为RandomIntGenerator的重复方法RandomIntGenerator(int) 。 怎么了? 问题在于,根据定义,构造函数没有名称。 因此,一个类只能具有一个具有给定签名的构造函数,而不能具有两个具有相同签名的方法(相同的返回类型,名称和参数类型)。 这就是为什么当我们尝试添加RandomIntGenerator(int max)构造函数时会出现该编译错误的原因,因为我们已经有了RandomIntGenerator(int min)一个。

在这种情况下,我们可以做些什么吗? 不是构造函数,但幸运的是,我们可以使用其他方法静态工厂方法 ,它们只是返回类实例的公共静态方法。 您可能没有意识到就使用了这种技术。 您曾经使用过Boolean.valueOf吗? 看起来像:

public static Boolean valueOf(boolean b) {return (b ? TRUE : FALSE);}

将静态工厂应用于我们的RandomIntGenerator示例,我们可以获得:

public class RandomIntGenerator {private final int min;private final int max;private RandomIntGenerator(int min, int max) {this.min = min;this.max = max;}public static RandomIntGenerator between(int max, int min) {return new RandomIntGenerator(min, max);}public static RandomIntGenerator biggerThan(int min) {return new RandomIntGenerator(min, Integer.MAX_VALUE);}public static RandomIntGenerator smallerThan(int max) {return new RandomIntGenerator(Integer.MIN_VALUE, max);}public int next() {...}
}

请注意,如何使构造函数私有化,以确保仅通过其公共静态工厂方法实例化该类。 还要注意,当您的客户端使用RandomIntGenerator.between(10,20)而不是new RandomIntGenerator(10,20)时,如何清楚地表达您的意图。 值得一提的是,该技术与“ 四人帮”中的Factory方法设计模式不同。 任何类都可以提供静态工厂方法来代替构造函数或在构造函数之外提供静态方法。 那么,这种技术的优缺点是什么? 我们已经提到了静态工厂方法的第一个优点:与构造函数不同,它们有名称。 这有两个直接的后果,

  1. 我们可以为构造函数提供一个有意义的名称。
  2. 我们可以为多个构造函数提供相同数量和类型的参数,就像我们之前看到的那样,我们无法使用类构造函数。

静态工厂的另一个优点是,与构造函数不同,静态工厂不需要在每次调用时都返回新对象。 当使用不可变的类为常用值提供常量对象并避免创建不必要的重复对象时,这非常有用。 我之前显示的Boolean.valueOf代码很好地说明了这一点。 请注意,此静态方法返回TRUEFALSE ,这两个都是不可变的布尔对象。

静态工厂方法的第三个优点是,它们可以返回其返回类型的任何子类型的对象。 这使您可以自由更改退货类型而不会影响客户。 此外,您可以隐藏实现类并拥有基于接口的API ,这通常是一个好主意。 但是我认为可以通过一个例子更好地看出这一点。

还记得本文开头的RandomIntGenerator吗? 让我们稍微复杂一点。 想象一下,我们现在不仅要为整数提供随机数生成器,还要为其他数据类型(如String,Double或Long)提供随机数生成器。 它们都将具有next()方法,该方法返回特定类型的随机对象,因此我们可以从如下接口开始:

public interface RandomGenerator<T> {T next();
}

现在,我们对RandomIntGenerator第一个实现变为:

class RandomIntGenerator implements RandomGenerator<Integer> {private final int min;private final int max;RandomIntGenerator(int min, int max) {this.min = min;this.max = max;}public Integer next() {...}
}

我们还可以有一个String生成器:

class RandomStringGenerator implements RandomGenerator<String> {private final String prefix;RandomStringGenerator(String prefix) {this.prefix = prefix;}public String next() {...}
}

请注意,如何将所有类声明为包专用(默认范围),以及它们的构造函数也是如此。 这意味着包之外的任何客户端都无法创建这些生成器的实例。 那么我们该怎么办? 提示:它以“静态”开始,以“方法”结束。
考虑以下类别:

public final class RandomGenerators {// Suppresses default constructor, ensuring non-instantiability.private RandomGenerators() {}public static final RandomGenerator<Integer> getIntGenerator() {return new RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE);}public static final RandomGenerator<String> getStringGenerator() {return new RandomStringGenerator('');}
}

RandomGenerators只是一个RandomGenerators实用程序类,除了静态工厂方法外别无其他。 与不同的生成器位于同一个包中,该类可以有效地访问和实例化这些类。 但是有趣的部分到了。 请注意,这些方法仅返回RandomGenerator接口,而这正是客户端真正需要的。 如果他们获得了RandomGenerator<Integer>他们知道可以调用next()并获得一个随机整数。
想象一下,下个月我们将编写一个超级高效的新整数生成器。 只要这个新类实现了RandomGenerator<Integer>我们就可以更改静态工厂方法的返回类型,并且所有客户端现在都在神奇地使用新实现,而他们甚至没有注意到更改。

在JDK和第三方库上,诸如RandomGenerators类的类都很常见。 您可以在Guava的 Collections (在java.util中), ListsSetsMaps中查看示例。 命名约定通常是相同的:如果您有一个名为Type的接口,则将您的静态工厂方法放在一个名为Types不可实例化的类中。

静态工厂的最后一个优点是,它们使实例化参数化的类的详细程度降低了很多。 您是否曾经不得不编写过这样的代码?

Map<String, List<String>> map = new HashMap<String, List<String>>();

您在同一行代码上重复相同的参数两次。 如果可以从左侧推断出分配的右侧,那会很好吗? 好吧,有了静态工厂就可以。 以下代码摘自Guava的Maps类:

public static <K, V> HashMap<K, V> newHashMap() {return new HashMap<K, V>();}

因此,现在我们的客户代码变为:

Map<String, List<String>> map = Maps.newHashMap();

很好,不是吗? 此功能称为类型推断 。 值得一提的是,Java 7通过使用菱形运算符引入了类型推断。 因此,如果您使用的是Java 7,则可以将前面的示例编写为:

Map<String, List<String>> map = new HashMap<>();

静态工厂的主要缺点是无法扩展没有公共或受保护的构造函数的类。 但这在某些情况下实际上可能是一件好事,因为它鼓励开发人员偏向于继承而不是继承 。

总而言之,静态工厂方法提供了很多好处,但只有一个缺点,当您考虑它时,这实际上可能不是问题。 因此,抵制自动提供公共构造函数并评估静态工厂是否更适合您的类的冲动。

参考: 开发时我们的JCG合作伙伴 Jose Luis的静态工厂方法与传统构造方法的比较 ,它应该是博客。

翻译自: https://www.javacodegeeks.com/2013/01/static-factory-methods-vs-traditional-constructors.html

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

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

相关文章

python输入代码界面通常_vscode写python时的代码错误提醒和自动格式化的方法

python的代码错误检查通常用pep8、pylint和flake8&#xff0c;自动格式化代码通常用autopep8、yapf、black。这些工具均可以利用pip进行安装&#xff0c;这里介绍传统的利用pip.exe安装和在VScode中安装两种方式。【温馨提醒】要使用flake8或要想flake8等工具起作用&#xff0c…

HTML/CSS 知识点

本文是从简书复制的, markdown语法可能有些出入, 想看"正版"和更多内容请关注 简书: 小贤笔记 整个前端开发的工作流程 产品经理提出项目需求UI出设计稿前端人员负责开发静态页面(跟前端同步的后台人员在准备数据)前后台的交互测试产品上线(后期项目维护) 互联网原…

枚举枚举和修改“最终静态”字段的方法

在本新闻通讯中&#xff0c;该新闻通讯最初发表在Java专家的新闻通讯第161期中&#xff0c;我们研究了如何使用sun.reflect包中的反射类在Sun JDK中创建枚举实例。 显然&#xff0c;这仅适用于Sun的JDK。 如果需要在另一个JVM上执行此操作&#xff0c;则您可以自己完成。 这一…

java编译找不到符号_关于久违的Javac,编译出现“找不到符号”

参考文档&#xff1a;http://blog.csdn.net/qq369201191/article/details/49946609工作以来习惯了maven编译&#xff0c;已经忘记了javac这个东东&#xff0c;以至于遇到javac问题时困惑了&#xff0c;下面总结一下&#xff0c;以便后者参考。一、使用javac进行项目java文件编译…

CSS--居中方式总结

一、水平居中方法 1.行内元素、字体的水平居中 1.对于行内元素&#xff08;display值为inline或inline-block都可以&#xff09;或者字体&#xff1a;父元素添加css规则&#xff1a;text-align&#xff1a;center; <style> p{/*关键*/text-align:center; }ul{/*关键*/…

009-MailUtils工具类模板

版本一&#xff1a;JavaMail的一个工具类 package ${enclosing_package};import java.security.GeneralSecurityException; import java.util.Properties;import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.ma…

HTML5 Video标签

1.代码格式 <video width"320" height"240" controls><source src"movie.mp4" type"video/mp4"><source src"movie.ogg" type"video/ogg">您的浏览器不支持Video标签。</video>视频格式及…

某些小时后MySql连接自动掉线

MySql配置为删除任何闲置超过8小时的连接。 这意味着什么&#xff1f; 在8个小时的间隔后返回到已部署的应用程序之后&#xff08;如果未更改默认SQL参数&#xff09;&#xff0c;将会遇到异常情况。 如何解决这个问题&#xff1f; 增加wait_time参数-不是一个好主意&#xff…

AutoMapper的使用

本来是想洋洋洒洒写点儿关于这个神奇的具体使用方法&#xff0c;但是发现园子里已经有了&#xff0c;URL奉上&#xff1a;http://www.cnblogs.com/CreateMyself/p/7635429.html&#xff0c;直接打开撸就行。转载于:https://www.cnblogs.com/pangjianxin/p/8367589.html

shopxx 阿里云OSS设置

shopxx 使用文档没有啊&#xff0c;只能自己看了 数据中心 字段其实是 EndPoint字段 URL前缀 是 图片服务器的主机地址。这个在阿里云回传的时候是不带的。 对应 阿里OSS 外网域名 转载于:https://www.cnblogs.com/nanahome/p/7346641.html

bio java 例子_JAVA BIO 服务器与客户端实现示例

代码只兼容JAVA 7及以上版本。服务器端代码&#xff1a;package com.stevex.app.bio;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import j…

我的HTML总结之常用基础便签

HTML&#xff1a;是Hyper Text Markup Language&#xff08;超级文本标记语言&#xff09;的缩写&#xff0c;HTML不是一种程序&#xff0c;只是一种控制网页中数据显示的标识语言。 HTML由一组标签组成。 HTML的基本结构 <html> <head> <title>第一个HTML示…

您是否应该信任JVM中的默认设置?

如今&#xff0c;JVM被认为是智能的。 预期不会进行太多配置–只需设置要在启动脚本中使用的最大堆&#xff0c;您就可以进行了。 所有其他默认设置都很好。 大概我们当中有些人误以为。 实际上&#xff0c;在运行时期间发生了很多事情&#xff0c;无法自动调整性能&#xff0c…

【翻译】A Next-Generation Smart Contract and Decentralized Application Platform

原文链接&#xff1a;https://github.com/ethereum/wiki/wiki/White-Paper 当中本聪在2009年1月启动比特币区块链时&#xff0c;他同时向世界引入了两种未经测试的革命性的新概念。第一种就是比特币&#xff08;bitcoin&#xff09;&#xff0c;一种去中心化的点对点的网上货币…

SAS Fuctions

1. monotonic(), 单调递增函数。返回一列变量的序列等&#xff0c;类似于_N_ 。 2. index v indexw: INDEX Function Searches a character expression for a string of characters, and returns the position of the string’s first character for the first occurrence of t…

循环内的局部变量和性能

总览 有时会出现一个问题&#xff0c;即分配一个新的局部变量需要花费多少工作。 我的感觉一直是&#xff0c;代码已优化到成本为静态的程度&#xff0c;即一次执行&#xff0c;而不是每次运行时都执行。 最近&#xff0c; Ishwor Gurung建议考虑将一些局部变量移出循环。 我怀…

CSS3伪元素、伪类选择器

伪元素选择器&#xff1a; ::first-letter:为某个元素中的文字的首字母或第一个字使用样式。 ::first-line:为某个元素的第一行文字使用样式。 ::before:在某个元素之前插入一些内容。 ::after:在某个元素之后插入一些内容 ::selection:匹配元素中被用户选中或处于高亮状态的部…

bzoj1212: [HNOI2004]L语言

这又是什么神题啊。 这题一眼AC机。然后呢企鹅也是这么想的。 写完发现企鹅看错题了。然后其实建字典树就行了。 弄个v数组表示能否匹配到第i个位置。然后因为字典里的串很短&#xff0c;就判一下前面L&#xff08;表示字典里最长那个串的长度&#xff09;个位置能否匹配&#…

css小随笔(二)与通用样式

51先在学校HTML5已经有半个多月了&#xff0c;然后这个星期做了一个京东的手机网站&#xff0c;接触到了通用样式&#xff0c;下面以京东的手机站为例 这两个就是京东手机站了的不同的两个板块&#xff0c;因为HTML5仅仅只是学完了基本标签跟css的标签&#xff0c;所以在没有接…

增加堆大小–谨防眼镜蛇效应

“眼镜蛇效应”一词源于英国殖民印度统治英国时所产生的轶事。 英国政府担心毒蛇眼镜蛇的数量。 因此&#xff0c;政府对每条死蛇给予悬赏。 最初&#xff0c;这是一个成功的策略&#xff0c;因为大量蛇被杀死以获取奖励。 最终&#xff0c;印度人开始养殖眼镜蛇以赚取收入。 …