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

我相信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前后台都做&…

福建计算机及应用专业的大学,33所福建大学公布国家级/省级一流本科专业!

近日&#xff0c;教育部发布《教育部办公厅关于公布2020年度国家级和省级一流本科专业建设点名单的通知》。据悉&#xff0c;本次教育部一流本科专业建设“双万计划”评选共认定了3977个国家级一流本科专业建设点&#xff0c;其中中央赛道1387个、地方赛道2590个&#xff0c;并…

高度有用的Java ChronoUnit枚举

几年前&#xff0c;我发表了博客文章“ The Highly有用的Java TimeUnit Enum ”&#xff0c;其中介绍了JDK 5引入的TimeUnit枚举。 JDK 8引入了一个更新的枚举ChronoUnit &#xff0c;它比TimeUnit更适合于诸如日期/时间操作等并发以外的上下文。 位于所述java.time.temporal包…

计算机管理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;该工具使…

有没有查计算机组成的答案得,计算机组成答案.doc

黄淮学院计算机科学系2008—2009年第二学期2007级《计算机组成原理》期终试卷(A)参考答案及评分标准命题人&#xff1a;王晓涓注意事项&#xff1a;本试卷适用于2007级软件工程专业本科学生使用一、单项选择题(每题1分、共15分)1、B 2、B 3、A 4、D 5、A 6、D 7、D 8、C 9、C 1…

使用JDK 8将收藏转换为地图

我多次遇到这样的情况&#xff0c;希望将多个对象存储在Map中而不是Set或List中&#xff0c;因为将唯一标识信息的Map应用于对象有一些优势 。 Java 8使用流和Collectors.toMap&#xff08;…&#xff09;方法使翻译变得比以往更加容易。 使用Map而不是Set的一种有用情况是&am…

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

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

车牌识别与计算机编程,基于计算机视觉的车牌字符识别技术的研究

摘要&#xff1a;车牌识别系统是现代智能交通管理系统的核心组成部分,是图像处理、神经网络和模式识别等技术的综合应用。 本文介绍了车牌识别系统的一般构成,包括车牌图像预处理、车牌字符分割和车牌字符识别等三个重要组成部分,并分别进行深入研究。 车牌图像预处理方面,针对…

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

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

用户需求测试软件,用户需求和软件需求的区别

原标题&#xff1a;用户需求和软件需求的区别一些刚实施GJB5000A组织的开发人员&#xff0c;不是很理解软件研制任务书和软件需求规格说明的区别。这里再次解读下这个问题。软件研制任务书就是用户需求&#xff0c;而用户需求是从用户的角度&#xff0c;用用户熟悉的语言进行描…

在Java中使用可信时间戳

受信任的时间戳记是使受信任的第三方&#xff08;“时间戳记权威”&#xff0c;TSA&#xff09;以电子形式证明给定事件的时间的过程。 欧盟法规eIDAS赋予了这些时间戳合法的力量-即&#xff0c;如果事件带有时间戳&#xff0c;则没有人可以质疑事件的时间或内容。 它适用于多种…

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

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

java配置openjdk_Java大新闻不断涌现:Java SE 6和OpenJDK

java配置openjdk我最近公布的最新的Java 8新闻 &#xff0c;总结了一些最近的事态发展中的Java 8 。 自从发布该文章不到一周&#xff0c;就发布了更多重要的Java新闻报道&#xff0c;我在本文中总结了其中的一些内容。 共同的主题包括不同的Java SE 6实现和OpenJDK 。 Java安…

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; 看书→算法公式推演→网上查询资料→进一步理解→搜…

编写自定义的AssertJ断言

AssertJ是广泛使用的Hamcrest匹配器的替代匹配库。 实际上&#xff0c;对于我自己的项目&#xff0c;我已经更改为仅使用AssertJ-我只是发现流畅的界面和可扩展性非常吸引人。 您可以编写自定义断言&#xff0c;如下所示&#xff1a; 想象一下一种具有强度和饮料类型的咖啡 &…

LintCode-A + B 问题

文章转载 http://blog.csdn.net/wangyuquanliuli/article/details/47755461 给出两个整数a和b, 求他们的和, 但不能使用 等数学运算符。 您在真实的面试中是否遇到过这个题&#xff1f; Yes样例 如果 a1 并且 b2&#xff0c;返回3 注意 你不需要从输入流读入数据&#xff0c;…