instanceof_您真的需要instanceof吗?

instanceof

使用instanceof是一种代码味道。 我认为我们可能对此表示同意。 每当我看到这样的构造时,我都会确定出现了问题。 也许有人只是在进行更改时没有注意到问题? 也许有一个主意,但是它太复杂了,以至于需要太多的精力或时间才能让开发人员决定不做呢? 也许只是懒惰? 谁知道。 事实仍然是代码演变成这种状态,我们必须与之合作。

或者也许我们可以做些什么? 有什么可以打开我们的扩展代码吗?

今天,我想向您展示如何实现这一目标。 但是首先,让我解释一下为什么这个instanceof根本是个问题。

看一下代码

今天我们将讨论以下代码:

public class ChangeProcessingHandler {public CodeDelta triggerProcessingChangeOf(Code code, Change change) {verifyChangeOf(code, change);if (change instanceof Refactoring) {return processRefactoring(code, (Refactoring) change);} else if (change instanceof Improvement)  {return processImprovement(code, (Improvement) change);} else if (change instanceof Growth) {return processGrowth(code, (Growth) change);} else {throw new UnsuportedChangeException();}}// some more code
}

我们将尝试对其进行改进。

我试图使这段代码具有描述性,但是让我简要地总结一下。 根据Change接口实现的特定类型,我们选择一种准确的处理方式。 如果找不到匹配的类型,我们将抛出一个异常。

现在,让我们看一下这段代码有什么问题。

接口及其实现?

当您查看方法的声明时,您能怎么说呢? 确实需要两个输入参数。 它给我们什么样的信息? 我们知道依赖关系,并基于它们的API,我们知道如何在方法主体中与传递的对象进行交互。

在给定的例子中是真的吗? 不幸的是没有。 我们正在传递一个Change实例,我们希望方法的主体将取决于其接口。 但是在内部,我们将实例转换为特定类型,这导致依赖性增加。

这本身不是一个好的设计决策,但更糟糕的是–我们在幕后增加了这个数字。 直到您不阅读方法的主体,您都不会知道。

缺乏知识远比依赖数量严重得多。

新类型不是那么容易添加

假设您必须添加Change接口的新实现。 会发生什么? 好吧,什么都没有。 您将添加类定义并对其进行测试。 您将运行所有测试。 如果至少有一个组件或系统测试能够通过新引入的Change接口实现达到所提供的代码并且失败,您将很幸运。

当没有这样的测试时,问题就开始了,您甚至都不知道应该改变某个地方以适应新功能。

一切都会编译,您将一直工作到……

为什么?

您在代码中注意到了这个不错的UnsupportedChangeException吗? 老实说,它在那里只是因为设计错误。

我们拥有它有两个原因:

  • 没有它,代码将无法编译。 当然,如果方法无效,我们可以跳过它,但是在我们的示例中,我们必须返回或抛出一些东西。 我们可以用其他方法代替last if-else,但这不是我们想要做的。
  • 它使我们无法添加新类型,而忘记在其中添加对新引入功能的支持。 假设至少有一种测试会在这种情况下失败。

为什么我称其为错误的设计? 很好,使用异常来表示需要支持新功能,这是对异常的滥用。 我还相信,如果我们的代码通过不编译来发出信号,那会更好。 这对我来说很有意义,而且肯定会提供更快的反馈。

来访者进行抢救!

访问者允许我们添加其他功能,其实现取决于对象的特定类型。 它允许使用接口的方法。 因此,我们可以避免自己检索有关特定接口的实现的信息。

首先,我们需要使检索有关对象类型的信息成为可能。 为此,我们必须在接口中添加一种方法,该方法将允许我们传递访问者:

public interface Change {void accept(Visitator visitator);
}

实现接口的每个对象的实现非常简单:

public class Refactoring implements Change {@Overridepublic void accept(Visitator visitator) {visitator.visit(this);}// some code
}

通过查看调用方法visit()的行,我们可以观察到什么? 这是检索有关类型的信息的地方。 不需要instanceof,也不需要强制转换。 这是我们在更好的设计支持下免费获得的东西。

这时,您可能知道Visitor的界面如下所示:

public interface Visitator {void visit(Refactoring refactoring);void visit(Improvement improvement);void visit(Growth growth);
}

不是那么复杂,不是吗?

之后,我们必须从ChangeProcessingHandler类中提取一些代码到实现Visitor接口的类中:

public class ChangeProcessor implements Visitator {private final Code code;public ChangeProcessor(Code code) {this.code = code;}@Overridepublic void visit(Refactoring refactoring) {// some code}@Overridepublic void visit(Improvement improvement) {// some code}@Overridepublic void visit(Growth growth) {// some code}
}

当然,我们必须在正确的位置使用它:

public class ChangeProcessingHandlerRefactored {public void triggerProcessingChangeOf(Code code, Change change) {verifyChangeOf(code, change);change.accept(new ChangeProcessor(code));}
}

是不是更好?

好的,所以我们更改了原始代码。 现在让我解释一下我们获得了什么。

  • 我们刚刚摆脱了一个例外。 不再需要它,因为对新引入的实现的必要支持将通过非编译代码发出信号。
  • 快速反馈是使用接口的结果,该接口将告诉我们要完全支持所有功能还需要实现什么。
  • 单一责任原则之所以起作用,是因为“访客”界面的每个特定实现仅负责一项功能。
  • 设计是面向行为的(接口),而不是面向实现的(instanceof + cast)。 这样,我们隐藏了实现细节。
  • 设计是开放的扩展。 引入易于实现的新功能真的很容易,这些功能针对特定对象而有所不同。

它不是那么完美

每个设计都是一个权衡。 您得到了一些东西,但这是有代价的。

我在上段中列出了收益,那么成本呢?

  • 这么多的对象
    可能有人说这是使用任何设计模式的明显结果,我会说是的。 但是,这并不能改变以下事实:随着对象数量的增加,在对象之间导航变得更加困难。
    将所有内容都放在一个对象中可能是一个问题,但是名称不正确或杂乱无章的类可能会导致混乱。
  • 复杂
    所有这些对象都需要一个名称,如果这些对象与域相关,那就太好了。 在这种情况下,我们最终会对我们的应用程序有了更好的了解。 但这并非总是如此。
    同样,我们在命名新引入的类时也必须非常小心。 所有这些都必须以不言自明的方式命名。 这并不像某些人想象的那么容易。
  • 我的(受限)上下文在哪里?
    访客可以帮助解决与示例中出现的问题类似的问题。 但是,如果有很多类似的地方,您必须意识到每个访问者都在某种程度上将对象的行为放入另一个对象。 得墨meter耳定律呢? 那告诉泰勒,不要问吗?
    在使用访客解决instanceof问题之前,您应该问自己这个功能是否不是对象本身的一部分? 一些开发人员向我解释这是拥有小对象的一种方式。 好吧,对我来说,这样的解释证明了我们应该考虑绑定上下文 。 对象仍然很小,它们的行为也不会泄漏给外部类。

就这样,伙计们

今天就这些。 我希望您发现重新设计的想法很有用,并且在阅读本文之后,您的代码中的异味肯定会受到威胁。 与往常一样,我鼓励您发表评论并分享您的观点和经验。 也许您更多地了解与此类变更相关的收益/问题。

翻译自: https://www.javacodegeeks.com/2016/10/really-need-instanceof.html

instanceof

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

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

相关文章

HTML 中的节点、元素、标签、标记的区别

文章目录一、HTML 节点(一)节点的类型二、HTML 标签三、HTML 元素(一)HTML 元素属性(二)HTML 元素的嵌套一、HTML 节点 节点(node)是构成我们网页的最基本的组成部分,网…

java查题小程序_求助大神来写一个java的小程序,题目如下

货不多说直接代码public class car {private String car_number;private float car_price;private float rebate;//折扣public float getRebate() {return rebate;}public void setRebate(float rebate) {this.rebate rebate;}public String getCar_number() {return car_numb…

valhalla 插件_Valhalla项目的价值

valhalla 插件我对Valhalla项目的进度一直很感兴趣,但是Brian Goetz最近的信息“ Valhalla项目:目标 ”提高了我的兴趣。 我经常喜欢Goetz的作品,因为他结合了我最想成为技术作者的两个特征:他比他所写的内容更了解他所写的主题&a…

cpu占用100_Java项目服务器cpu占用100%解决办法

1、使用命令top查看cpu进程,发现tomcat占用cpu使用率过高可以看到服务器负载很高,tomcat CPU使用已达到接近300%,内存20%。2、把进程的栈dump到文件里,以便后面的分析jstack 22764 > cpu0827.log 3、看看这个进程里面哪些线程在占用cputop…

MySQL 获取系统时间/系统日期/日期时间的函数

文章目录一、获取当前系统日期和时间(一)now(),返回当前的系统日期和时间(二)sysdate(),返回当前的系统日期和时间(三)current_timestamp(),返回当前的系统日期和时间&am…

java人种_实在看不出藏族有大量矮黑血统

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼人种混合的存在提醒了研究远东地区的人类学家。在远东,特别是东南亚,棕色人种和黄色人种错综而居,有些群体更似乎兼有两方的特征,很难判定。这种格局究竟是不是混血,还是其…

apm java_非Java专家的APM:什么泄漏?

apm java通过AppDynamics解决应用程序问题的速度提高了10倍–以最小的开销在代码级深度监视生产应用程序。 开始免费试用! 内存,内存,内存... 内存是Java的关键部分,尤其是内存管理。 作为开发人员,内存管理不是您想…

360安全卫士电脑版_教你降服“流氓头子”正确打开360安全卫士的姿势,还你电脑流畅体验...

最近老哥家的电脑经常出问题原因是熊孩子玩儿完电脑,最后总是一堆莫名其妙的流氓软件占据桌面的半壁江山然后恢复电脑流畅运行的任务,就交到我这个“懂电脑”的人手里。如此反复两次,脾气再好的人,也想拉着熊孩子打一顿。但是活儿…

Linux命令终端如何查询本机的内外网IP地址

文章目录查询本机外网 IP 地址查询本机内网 IP 地址查询本机外网 IP 地址 [roothtlwk0001host ~]# curl ifconfig.me # 太慢了,不行[roothtlwk0001host ~]# curl icanhazip.com 47.114.59.104[roothtlwk0001host ~]# curl ident.me # 太慢了,不行[rooth…

信封问题_装饰信封

信封问题有时 很多时候,我需要一个类实现通过使其他类的实例的接口。 听起来很奇怪? 让我给你看一个例子。 在Takes框架中有许多此类,它们的名称都都类似于*Wrap 。 不幸的是,这是一个方便的设计概念,在Java中看起来很…

java netty swap高_Netty 超时机制及心跳程序实现

本文介绍了 Netty 超时机制的原理,以及如何在连接闲置时发送一个心跳来维持连接。Netty 超时机制的介绍Netty 的超时类型 IdleState 主要分为:ALL_IDLE : 一段时间内没有数据接收或者发送READER_IDLE : 一段时间内没有数据接收WRITER_IDLE &a…

MySQL查询条件中的各种运算符/操作符说明

文章目录一、比较运算符&#xff08;一&#xff09;正则表达式&#xff08;二&#xff09;模糊匹配 LIKE&#xff08;三&#xff09;转义字符二、单行比较运算符&#xff08;一&#xff09;安全等号 <>1. 和 的相同点2. 和 的不同点&#xff08;二&#xff09;区间运算…

etf基金代码大全_银行ETF最新规模首超28亿元再创历史新高,近4个月资金净流入超12亿元...

2019年即将收官&#xff0c;A股大盘在12月份总体有了明显的回暖&#xff0c;拥有低估值优势的银行板块随之明显反弹。受此利好行情催化&#xff0c;资金持续涌入银行ETF&#xff0c;其基金份额、基金规模双双连创历史新高。据上交所12月26日公布的最新数据&#xff0c;银行ETF&…

java sendmessage函数_vc中SendMessage自定义消息函数用法实例

本文实例讲述了vc中SendMessage自定义消息函数用法&#xff0c;分享给大家供大家参考。具体如下&#xff1a;SendMessage的基本结构如下&#xff1a;SendMessage(HWND hWnd, //消息传递的目标窗口或线程的句柄。UINT Msg, //消息类别(这里可以是一些系统消息&#xff0c;也可以…

你为什么要关心equals和hashcode

等于和哈希码是每个Java对象的基本元素。 它们的正确性和性能对于您的应用程序至关重要。 但是&#xff0c;我们经常看到甚至有经验的程序员都忽略了类开发的这一部分。 在本文中&#xff0c;我将介绍一些与这两种非常基本的方法有关的常见错误和问题。 合同 提到的方法至关重…

java判断时间是否在时间段内_具有C语言基础,利用半年时间学习Java是否够用

首先&#xff0c;对于具有一定C语言基础的同学来说&#xff0c;利用半年的时间来学习Java会有一个比较系统的学习过程&#xff0c;通常也能够达到实习程序员&#xff0c;或者是助理程序员的水平&#xff0c;然后在技术团队中实践一段时间之后&#xff0c;从事专业开发岗位应该是…

Liao Wenxiong‘s thoughts

点击看看在想什么鬼

python3字符串截取_从python3中的字符串中获取特定文本

只是使用str.split()和赋值给变量,还有一个默认的第二个参数可以帮助您处理唯一的total/free avail场景def get_free_memory(ssh_obj, rawTrue):stdin, stdout, stderr ssh_obj.exec_command(free -h)mem_stats stdout.readlines()[1]if raw:mem_stats_formatted " &qu…

kafka connect_Kafka Connect在MapR上

kafka connect在本周的白板演练中&#xff0c;MapR的高级产品营销经理Ankur Desai描述了Apache Kafka Connect和REST API如何简化和提高在处理来自包括旧数据库或数据仓库在内的各种数据源的流数据时的敏捷性。 他还解释了当您使用MapR Streams与Kafka进行数据传输时&#xff0…

二维动画作品_「咻动画」二维动画制作中角色造型的设计要点

关于二维动画可能就算不是动画行业的小伙伴们都多多少少都有所了解&#xff0c;近年来其在宣传片制作上面越来越受用。不少企业抛开传统保守的宣传片表现形式转而尝试动画制作宣传片&#xff0c;我们都知道在动画制作从脚本策划到输出成片这个过程中是一个很复杂的制作流程&…