静态工厂方法代替构造器实例_静态工厂方法与传统构造方法

静态工厂方法代替构造器实例

之前,我已经讨论过一些关于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/349677.shtml

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

相关文章

NOIP模拟测试21「折纸·不等式」

折纸 题解 考试时无限接近正解&#xff0c;然而最终也只是接近而已了 考虑模拟会爆炸&#xff0c;拿手折纸条试一试&#xff0c;很简单 考你动手能力 代码 #include<bits/stdc.h> using namespace std; #define ll long long #define A 3100000 ll cj[A],questions[A]; l…

小孔成像总结_科学实践小孔成像

科学实践小孔成像-徐征浩、周永锋耳闻之不如目见之&#xff0c;目见之不如足践之。行是知之始&#xff0c;知是行之成。聪明不仅仅是理论上&#xff0c;它还体现在实践等方面。国庆期间&#xff0c;同学们运用物理课堂上所学的知识制作了小孔成像和土电话这些常见的物理仪器&am…

休眠架构概述

下图提供了Hibernate体系结构的高级视图&#xff1a; 最小的架构 “最小”架构使应用程序可以管理自己的JDBC连接&#xff0c;并提供与Hibernate的连接。 另外&#xff0c;应用程序自行管理交易。 这种方法使用了最少的Hibernate API子集。 综合架构 Hibernate Basic API 这里…

ASP.NET Core MVC 之过滤器(Filter)

ASP.NET MVC 中的过滤器允许在执行管道中的特定阶段之前或之后运行代码。可以对全局&#xff0c;也可以对每个控制器或每个操作配置过滤器。 1.过滤器如何工作 不同的过滤器类型在管道中的不同阶段执行&#xff0c;因此具有各自的与其场景。根据需要执行的任务以及需要执行的请…

.sh文件是什么语言_FastDFS分布式文件系统的搭建安装

FastDFS很久之前&#xff0c;对上传的文件可能是存储在数据库&#xff0c;在数据库中存储路径&#xff0c;保存文件的二进制数据&#xff0c;随着用户上传的文件增加&#xff0c;数据库数据越来越多。这时&#xff0c;可以使用分布式文件系统&#xff0c;将用户上传的文件例如图…

ASP.NET Core MVC 之依赖注入 View

ASP.NET Core 支持在试图中使用依赖注入。这将有助于提供视图专用的服务&#xff0c;比如本地化或者仅用于填充视图元素的数据。应尽量保持控制器和视图之间的关注点分离。视图所显示的大部分数据应该从控制器传入。 使用 inject 指令将服务注入到视图&#xff0c;语法 inject …

matlab全安装多大_不理会其他,我只中意T20天正V2.0软件,激活安装教程在这里...

大家好&#xff0c;我是本文的小编 软妹&#xff0c;前几篇为大家分享了Proe系列软件的安装步骤&#xff0c;在设计三维设计领域Pro/E是现今主流的CAD/CAM/CAE软件一款软件&#xff0c;特别是在国内产品设计领域占据重要位置。在数控加工编程软件领域&#xff0c;哪款软件才是王…

couchbase_Couchbase 2.0归类视图简介

couchbase大多数应用程序必须处理“主/详细”类型的数据&#xff1a; 啤酒厂和啤酒 部门和员工 发票和项目 … 例如&#xff0c;创建如下所示的应用程序视图是必需的&#xff1a; 借助Couchbase和许多面向文档的数据库&#xff0c;您可以使用不同的方式来处理此问题&…

NOIP模拟测试20「周·任·飞」

liu_runda出的题再次$\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%\%$ 任 题解 题目中为什么反复强调简单路径,没有环 没有环的图中点数-边数联通块数 前缀和维护边的前缀和,和点的前缀和, 在维护边的前缀和不好维护转化为横着边前缀和,竖着边前缀和 注意边的边界问题 看边如何维护 就拿…

如何脚踏实地构建Java Agent

在构建Plumbr的多年中&#xff0c;我们遇到了许多具有挑战性的问题。 在其他方面&#xff0c;使Plumbr Java Agent可靠地执行而又不危害客户的应用程序&#xff0c;是一个特别棘手的任务。 从实时系统中安全地收集所有需要的遥测会带来很多问题。 其中一些非常简单&#xff0c;…

ASP.NET Core MVC 之依赖注入 Controller

ASP.NET Core MVC 控制器应通过构造函数明确地请求它们地依赖关系&#xff0c;在某些情况下&#xff0c;单个控制器地操作可能需要一个服务&#xff0c;在控制器级别上的请求可能没有意义。在这种情况下&#xff0c;也可以将服务作为 Action 的参数。 依赖注入是一种如 Depend…

16g内存 32g内存游戏区别_电脑内存8G和16G的差别大吗?打游戏需要多大内存?

Hello大家好&#xff0c;我是兼容机之家的小牛。电脑内存是除了CPU之外最重要的元件之一&#xff0c;电脑内存的多少直接影响着运行大型软件、多任务时电脑的流畅程度&#xff0c;很多玩家在第一次购买电脑的时候并不知道应该购买内存多大的电脑&#xff0c;今天小牛就来和大家…

用友U9执行JS代码。

UFSoft.UBF.UI.AtlasHelper.RegisterAtlasStartupScript(part.Page, part.Page.GetType(), "JavaScriptExecQueue", "alert(地球即将毁灭&#xff0c;进入倒计时&#xff1a;5&#xff0c;4&#xff0c;3&#xff0c;2&#xff0c;1…嘣&#xff01;嘣&#xff…

java培训学费_北京Java培训班学费很贵吗,包含了哪些收费项目

北京的Java培训班有很多&#xff0c;价格却是相差不多的&#xff0c;但培训的课程就参差不齐了&#xff0c;有的培训班就是为了赚钱而存在的&#xff0c;想要系统的学习Java&#xff0c;确保学习效果&#xff0c;那么你一定要挑选正规的Java培训班&#xff0c;挑选适合自己的Ja…

typora.io使用教程

引言&#xff1a;对于开发人员大部分都接触过.md文件&#xff0c;而typora.io就是专门编辑.md文件的工具&#xff0c;该工具对于编写接口文档特别方便&#xff0c;它提供了word类似的大纲视图&#xff0c;同时也提供了很多的功能&#xff0c;但是改软件本身却非常的小&#xff…

c#编译时提高兼容性_幻像类型提高了编译时的安全性

c#编译时提高兼容性介绍 使用幻像类型是一种非常简单的技术&#xff0c;可用于提高代码的编译时安全性。 有很多潜在的用例&#xff0c;其复杂性程度各不相同&#xff0c;但是即使幻像类型的轻量级使用也可以显着提高编译时的安全性。 幻像类型只是带有未使用类型参数的参数化类…

如何查看电脑显卡配置_3080显卡电脑配置清单(3700X/10700)

3080显卡昨天就解禁评测了&#xff0c;因为最近的老铁都很期待装3080的机器&#xff0c;我这边做了一些整理&#xff0c;方便各位老铁3080配置有综合的了解。3080显卡售价更加详尽的3080厂家整理可以参考&#xff1a;3080公版哪里买&#xff1f;&#xff08;所有厂家整理&#…

问题 1074: 数字整除

题目描述定理&#xff1a;把一个至少两位的正整数的个位数字去掉&#xff0c;再从余下的数中减去个位数的5倍。当且仅当差是17的倍数时&#xff0c;原数也是17的倍数 。 例如&#xff0c;34是17的倍数&#xff0c;因为3-20-17是17的倍数&#xff1b;201不是17的倍数&#xff0c…

GWT HTTP请求替代

由于多种原因 &#xff0c;许多GWT用户放弃了RPC机制&#xff0c;这是GWT提供的调用后端的标准方法。 他们发现&#xff0c;在GWT RequestBuilder与其他可能不适合其应用程序模型的外部库之间迷失了许多 。 这篇文章的目的是通过GWT中众所周知的HTTP / Rest库&#xff0c;以使情…