构造函数还是静态工厂方法?

我相信Joshua Bloch在他的非常好的书“ Effective Java”中首先说了它:与构造函数相比,静态工厂方法是实例化对象的首选方法。 我不同意。 不仅因为我相信静态方法是纯粹的邪恶,而且主要是因为在这种特殊情况下,它们伪装成好的方法,使我们认为我们必须爱上它们。

摘录(2009),作者:麦克·贾奇(Mike Judge)

让我们从面向对象的角度分析推理并查看其原因。

这是一个具有一个主要构造函数和两个次要构造函数的类:

class Color {private final int hex;Color(String rgb) {this(Integer.parseInt(rgb, 16));}Color(int red, int green, int blue) {this(red << 16 + green << 8 + blue);}Color(int h) {this.hex = h;}
}

这是带有三个静态工厂方法的类似类:

class Color {private final int hex;static Color makeFromRGB(String rgb) {return new Color(Integer.parseInt(rgb, 16));}static Color makeFromPalette(int red, int green, int blue) {return new Color(red << 16 + green << 8 + blue);}static Color makeFromHex(int h) {return new Color(h);}private Color(int h) {return new Color(h);}
}

你更喜欢哪一个?

据约书亚布洛赫,但使用静态工厂方法而不是构造函数(一共设置了四个,但第四个是不是适用于Java的三个基本优势了 ):

  • 他们有名字。
  • 他们可以缓存。
  • 它们可以是子类型。

我认为,如果设计错误,那么这三者都是完全合理的。 它们是解决方法的好借口。 让我们一一介绍。

他们有名字

这是使用构造函数制作红色番茄颜色对象的方法:

Color tomato = new Color(255, 99, 71);

这是使用静态工厂方法执行的操作:

Color tomato = Color.makeFromPalette(255, 99, 71);

看起来makeFromPalette()在语义上比new Color()更丰富,对吗? 嗯,是。 如果我们将它们传递给构造函数,谁知道这三个数字意味着什么。 但是“调色板”一词可以帮助我们立即解决所有问题。

真正。

但是,正确的解决方案是使用多态和封装,以将问题分解为几个语义丰富的类:

interface Color {
}
class HexColor implements Color {private final int hex;HexColor(int h) {this.hex = h;}
}
class RGBColor implements Color {private final Color origin;RGBColor(int red, int green, int blue) {this.origin = new HexColor(red << 16 + green << 8 + blue);}
}

现在,我们使用正确的类的正确的构造函数:

Color tomato = new RGBColor(255, 99, 71);

看,约书亚?

他们可以缓存

假设我在应用程序中的多个位置需要一个红色的番茄色:

Color tomato = new Color(255, 99, 71);
// ... sometime later
Color red = new Color(255, 99, 71);

将创建两个对象,这显然是低效的,因为它们是相同的。 最好将第一个实例保留在内存中的某个位置,并在第二个调用到达时将其返回。 静态工厂方法可以解决这个问题:

Color tomato = Color.makeFromPalette(255, 99, 71);
// ... sometime later
Color red = Color.makeFromPalette(255, 99, 71);

然后,在Color内的某个地方,我们保留了一个私有静态Map ,其中已实例化了所有对象:

class Color {private static final Map<Integer, Color> CACHE =new HashMap<>();private final int hex;static Color makeFromPalette(int red, int green, int blue) {final int hex = red << 16 + green << 8 + blue;return Color.CACHE.computeIfAbsent(hex, h -> new Color(h));}private Color(int h) {return new Color(h);}
}

这是非常有效的性能。 对于像我们的Color这样的小对象,问题可能并不那么明显,但是当对象较大时,其实例化和垃圾回收可能会浪费大量时间。

真正。

但是,有一种面向对象的方法可以解决此问题。 我们只是介绍了一个新类Palette ,它变成了一个颜色存储区:

class Palette {private final Map<Integer, Color> colors =new HashMap<>();Color take(int red, int green, int blue) {final int hex = red << 16 + green << 8 + blue;return this.computerIfAbsent(hex, h -> new Color(h));}
}

现在,我们一次创建一个Palette实例,并要求它在每次需要时向我们返回一种颜色:

Color tomato = palette.take(255, 99, 71);
// Later we will get the same instance:
Color red = palette.take(255, 99, 71);

见,约书亚,没有静态方法,没有静态属性。

他们可以亚型

假设我们的Color类有一个lighter()方法,该方法应该将颜色转移到下一个可用的打火机上:

class Color {protected final int hex;Color(int h) {this.hex = h;}public Color lighter() {return new Color(hex + 0x111);}
}

但是,有时更希望通过一组可用的Pantone颜色选择下一种较浅的颜色:

class PantoneColor extends Color {private final PantoneName pantone;PantoneColor(String name) {this(new PantoneName(name));}PantoneColor(PantoneName name) {this.pantone = name;}@Overridepublic Color lighter() {return new PantoneColor(this.pantone.up());}
}

然后,我们创建一个静态工厂方法,该方法将决定哪种Color实现最适合我们:

class Color {private final String code;static Color make(int h) {if (h == 0xBF1932) {return new PantoneColor("19-1664 TPX");}return new RGBColor(h);}
}

如果要求使用真正的红色 ,我们将返回PantoneColor一个实例。 在其他所有情况下,它只是一个标准的RGBColor 。 该决定是通过静态工厂方法做出的。 这就是我们所说的:

Color color = Color.make(0xBF1932);

由于构造函数只能返回在其中声明的类,因此不可能对构造函数进行相同的“分叉”。静态方法具有返回Color任何子类型的所有必要自由。

真正。

但是,在面向对象的世界中,我们可以而且必须以不同的方式去做。 首先,我们将Color为接口:

interface Color {Color lighter();
}

接下来,我们将将此决策过程移至其自己的类Colors ,就像在上一个示例中所做的那样:

class Colors {Color make(int h) {if (h == 0xBF1932) {return new PantoneColor("19-1664-TPX");}return new RGBColor(h);}
}

而且我们将使用Colors类的实例,而不是Color内部的静态方法:

colors.make(0xBF1932);

但是,这仍然不是真正的面向对象的思维方式,因为我们正在将决策权从对象所属的对象转移开。 通过静态工厂方法make()或新类Colors实际上并不重要),我们将对象分成两部分。 第一部分是对象本身,第二部分是决策算法,它位于其他地方。

更加面向对象的设计是将逻辑放入PantoneColor类的对象中,该对象将装饰原始的RGBColor

class PantoneColor {private final Color origin;PantoneColor(Color color) {this.origin = color;}@Overridepublic Color lighter() {final Color next;if (this.origin.hex() == 0xBF1932) {next = new RGBColor(0xD12631);} else {next = this.origin.lighter();}return new PantoneColor(next);}
)

然后,我们创建一个RGBColor实例,并使用PantoneColor装饰它:

Color red = new PantoneColor(new RGBColor(0xBF1932)
);

我们要求red返回较浅的颜色,它返回Pantone调色板中的一种,而不是仅在RGB坐标中较浅的颜色:

Color lighter = red.lighter(); // 0xD12631

当然,这个示例是原始的,如果我们真的希望它适用于所有Pantone颜色,则需要进一步改进 ,但是我希望您能理解。 逻辑必须保留内部 ,而不是外部,静态工厂方法甚至其他补充类中。 当然,我在说的是属于这个特定类的逻辑。 如果与类实例的管理有关,那么可以有容器和存储,就像上面的上一个示例一样。

总而言之,我强烈建议您不要使用静态方法,尤其是当它们要替换对象构造函数时。 通过其构造函数生成对象是任何面向对象软件中最 “神圣”的时刻,请不要错过它的美丽。

您可能还会发现这些相关的帖子很有趣: 每个私有静态方法都是新类的候选人 ; 您是一个更好的建筑师,您的图表更简单 ; 只有一个主要的建设者 ; 为什么InputStream设计错误 ; 为什么在OOP中很多退货声明是个坏主意 ;

翻译自: https://www.javacodegeeks.com/2017/11/constructors-static-factory-methods.html

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

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

相关文章

html vue分页,Vue.js bootstrap前端实现分页和排序

写之前先抱怨几句。本来一心一意做.net开发的&#xff0c;渐渐地成了只做前端。最近项目基本都用java做后台&#xff0c;我们这些.net的就成了前端&#xff0c;不是用wpf做界面&#xff0c;就是用html写web页面。深知自己前端技术不足&#xff0c;以前虽说用asp.net前后台都做&…

计算机管理ap,如何设置AC功能管理无线AP

AC功能管理无线AP第一步、连接ap举例型号TL-ER6520G不同类型的AP供电方式可能有所差异&#xff0c;包括直流电源供电和网线供电(PoE供电)两种。如下[1] 直流电源供电使用自带的电源适配器给AP供电&#xff0c;要求AP安放位置必须有电源插座&#xff0c;优点是节省成本。按照拓扑…

代表机械 计算机技术的颜色,基于机器视觉识别的交通灯控制系统

1、引言机器视觉又称计算机视觉&#xff0c;是用计算机来实现人的视觉功能&#xff0c;也就是用机器代替人眼来做测量和判断。机器视觉技术包含光源照明技术、光成像技术、传感器技术、数字图像处理技术、机械工程技术、检测控制技术、模拟与数字视频技术、计算机技术、人机接口…

java开发者工具开源版_JArchitect对Java开源贡献者免费

java开发者工具开源版JArchitect是用于Java代码库的静态分析工具&#xff0c;它提供交互式GUI和HTML报告&#xff0c;用于查找代码中过于复杂或有问题的区域&#xff0c;执行分析以重构并比较随时间的变化。 在版本3中&#xff0c;添加了类似LINQ的查询语言&#xff0c;该工具使…

计算机上没有启动程序怎么办,Win7开机不加载启动项怎么办

现在的电脑为了受到更好的保护&#xff0c;往往在开机的时候就加载了一些启动项&#xff0c;如&#xff1a;杀毒软件&#xff0c;安全卫士等等。可有时候会发现&#xff0c;在Win7操作系统中&#xff0c;安装后发现不能开机加载启动项。在注册表中&#xff0c;在msconfig中&…

线程同步,线程不同步_重新同步多线程集成测试

线程同步,线程不同步我最近在Captain Debug的Blog上偶然发现了一篇文章“ 同步多线程集成测试 ”。 那篇文章强调了设计涉及异步运行业务逻辑的被测类的集成测试的问题。 给出了这个人为的示例&#xff08;我删除了一些评论&#xff09;&#xff1a; public class ThreadWrapp…

无限滚动重置服务器,简单无限滚动的实现

在使用elementUI组件库的时候&#xff0c;用到了无限滚动这个功能。我没有看源码&#xff0c;直接在网上学习了下实现的思路&#xff0c;然后自己手动编码以下。在此总结下。假设页面上有一个盒子容器&#xff0c;容器内有一些子元素。容器的高度是固定的&#xff0c;有纵向滚动…

Fatjars,Thinwars以及为什么OpenLiberty很酷

法特哈斯 构建一个Fatjar&#xff08;或Uberjar&#xff09;&#xff0c;其中包含将应用程序很好地打包在一起运行所需的一切&#xff0c;这意味着您可以&#xff1a; java -jar myapp.jar然后离开。 没有应用程序服务器。 没有类路径。 这种方法已经被诸如Springboot之类的微…

Spring Cloud Config Server简介

1.概述 在本教程中&#xff0c;我们将回顾Spring Cloud Config Server的基础知识。 我们将设置一个Config Server &#xff0c;然后构建一个客户端应用程序 &#xff0c;该客户端应用程序在启动时会消耗配置 &#xff0c;然后刷新配置而不重新启动。 我们正在构建的应用程序与《…

朴素贝叶斯算法实现分类以及Matlab实现

开始 其实在学习机器学习的一些算法&#xff0c;最近也一直在看这方面的东西&#xff0c;并且尝试着使用Matlab进行一些算法的实现。这几天一直在看得就是贝叶斯算法实现一个分类问题。大概经过了一下这个过程&#xff1a; 看书→算法公式推演→网上查询资料→进一步理解→搜…

位操作基础篇之位操作全面总结

转载自 http://blog.csdn.net/morewindows/article/details/7354571 Title: 位操作基础篇之位操作全面总结 KeyWord: C/C 位操作 位操作技巧 判断奇偶 交换两数 变换符号 求绝对值 位操作压缩空间 筛素数 位操作趣味应用 位操作笔试面试 位操作篇共分为基础篇和提高…

机器学习中的算法-支持向量机(SVM)基础

机器学习中的算法-支持向量机(SVM)基础 版权声明&#xff1a; 本文由LeftNotEasy发布于http://leftnoteasy.cnblogs.com, 本文可以被全部的转载或者部分使用&#xff0c;但请注明出处&#xff0c;如果有问题&#xff0c;请联系wheeleastgmail.com。也可以加我的微博: leftnotea…

算法题:输入aaaabbbcccccc输出a4b3c6。

今日在地铁上浏览今日头条的时候看到这么个小题目&#xff0c;说是输出一长串字符串&#xff0c;输出字母串类别并且统计其出现次数&#xff0c;然后按照顺序将其输出来。例如输入aaaabbbcccccc&#xff0c;输出a4b3c6。 最近也一直在学习&#xff0c;所以就想着就Matlab来试了…

Java World中的GraphQL简介

许多人认为GraphQL仅适用于前端和JavaScript&#xff0c;它在Java等后端技术中没有定位&#xff0c;但事实确实如此。 还经常将GraphQL与REST进行比较&#xff0c;但是这种比较是否合理&#xff1f; 首先&#xff0c;让我开始回答其中最重要的问题。 什么是GraphQL&#xff1…

算法题:在一个字符串中找到只出现一次的字符。如输入abaccdeeff,则输出bd。

今天的算法学习还是和字符串有关&#xff0c;这个题目据说是以前的某公司面试的笔试题目。题目意思就是说&#xff0c;在一个字符串中找到只出现了一次的那些字符&#xff0c;并且输出来。 作为非IT的我&#xff0c;平时使用Matlab比较多。不是科班出身&#xff0c;对于这个题…

Kafka的Spring Cloud Stream

总览 该示例项目演示了如何使用事件驱动的体系结构 &#xff0c; Spring Boot &#xff0c;Spring Cloud Stream&#xff0c; Apache Kafka和Lombok构建实时流应用程序。 在本教程结束时&#xff0c;您将运行一个简单的基于Spring Boot的Greetings微服务 从REST API获取消息 …

常见的股票技术因子学习以及计算

最近在看《量化投资数据挖掘技术与实践&#xff08;MATLAB版&#xff09;》。学习了其中的常见的股票衍生变量&#xff0c;并且利用WIND金融数据终端的matlab借口windmatlab导出一些数据进行了一个简单的学习。特此记录。 下面是我对于书中提到的几个因子的学习总结&#xff1…

Java – HashMap详细说明

HashMap基于哈希算法工作&#xff0c;根据Java文档HashMap具有以下四个构造函数&#xff0c; 建设者 描述 HashMap ​() 构造一个空的 具有默认初始容量&#xff08;16&#xff09;和默认加载因子&#xff08;0.75&#xff09;的HashMap 。 HashMap ​(int initialCapaci…

Python实现石头-剪刀-布小游戏

近日在学习Python的一些基础知识&#xff0c;觉得还是很有趣的一个一门语言&#xff01;就目前的学习的一些知识&#xff0c;编写了一些一个简单的石头剪刀布的游戏。主要是熟悉一些Python的一些控制语句。 import random while 1:sint(random.randint(1,3))print(s)print()if…

Python:递归输出斐波那契数列

今天学习Python的时候做一道练习题&#xff0c;题目是这样的&#xff1a; 题目 导入 问题 有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总对数为多…