您真的需要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

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

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

相关文章

python 累积正态分布函数_Python编程基础—Python语句书写规范

Python语句中没有专门的“结束符”。Python解释器不是根据"结束符"来判断语句是否结束,而是根据语法的完整性来判断。一、Python语句编写规则①通常是一行一句x1 1 x2 2 x3 3 print(x1,x2,x3)②也可以一行多句,用语句分隔符“;”对两个语句进行标识x1 …

MATLAB批量改变图片大小

%2018年6月28日11:07:15 %把一个目录下的图片缩放到指定大小 clc clear ratio 0.2;%缩放比例 cd(F:\数据集\crumpled clothes\cloth3\original image\);%不加这句话找不到图片 dpath F:\数据集\crumpled clothes\cloth3\original image\*.JPG;%找到路径下所有格式为.JPG的文件…

基于Libevent的HTTP Server

简单的Http Server 使用Libevent内置的http相关接口&#xff0c;可以很容易的构建一个Http Server&#xff0c;一个简单的Http Server如下&#xff1a; #include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h> #include <Winso…

python写入数据的一种措施_Python 文件数据读写的具体实现

文件数据读写读写文件&#xff0c;本质上是请求操作系统打开一个文件对象&#xff0c;然后&#xff0c;通过操作系统提供的接口从这个文件对象中读取数据(读文件)&#xff0c;或者把数据写入这个文件对象(写文件)。文件读取使用 Python 内置 open() 函数&#xff0c;以 rt 的模…

MATLAB的dir函数

1、作用获得指定文件夹下的所有子文件夹和文件,并存放在在一种文件结构体数组中. 2.使用方法dir(.)列出当前目录下所有子文件夹和文件dir(G:\Matlab)列出指定目录下所有子文件夹和文件dir(G:\Matlab\*.jpg)列出当前目录下符合正则表达式的文件夹和文件3.例如&#xff1a;列出文…

libevent简介和使用

libevent是一个基于事件触发的网络库&#xff0c;memcached底层也是使用libevent库&#xff0c;今天学习下。总体来说&#xff0c;libevent有下面一些特点和优势&#xff1a;* 统一数据源&#xff0c; 统一I/O事件&#xff0c;信号和定时器这三种事件&#xff1b;* 可移植&…

qr码生成_从Java程序生成QR码图像

qr码生成如果您精通技术和小工具&#xff0c;则必须了解QR码。 这些天&#xff0c;到处都可以找到它-在博客&#xff0c;网站&#xff0c;甚至在某些公共场所。 这在移动应用程序中非常流行&#xff0c;在移动应用程序中&#xff0c;您可以使用QR Code扫描仪应用程序扫描QR Cod…

jracdrive变频器说明书580_jracdrive变频器err02

A&#xff1a;高性能密封型变频器-防粉尘、防水、防油污、防腐蚀此款高性能变频器&#xff0c;因为其具有防粉尘、防水、防油污、防腐蚀性能&#xff0c;适用于化工厂、印染厂、化纤厂、纺织厂、输送带、机床、水泥厂、制*厂、食品厂等环境恶劣的场合。变频器硬件特点&#xff…

Harris角点检测+sift描述匹配

最近用到&#xff1a;Harris检测图像中边缘处的特征点&#xff0c;并且需要两张图特征点匹配起来。Harris只是一个角点检测的算法&#xff0c;最终只得到特征点坐标&#xff0c;想要匹配需要描述&#xff0c;而Harris是单尺度的&#xff0c;自己写描述函数又有些麻烦。找到MATL…

python辅助脚本教程_微信跳一跳python辅助脚本实例分享

本文主要为大家整理了关于微信跳一跳的辅助脚本内容&#xff0c;这次我们给大家整理的是关于python的脚本内容&#xff0c;一起来学习下。这段时间微信跳一跳这个游戏非常火爆&#xff0c;但是上分又非常的难&#xff0c;对于程序员来说第一个念头就是通过写一个辅助脚本外挂让…

Bug解决方案

一&#xff1a;百度错误代码或者关键字搜索 二&#xff1a;在博客论坛招贴问人 三&#xff1a;向同事或者公司老人请教 四&#xff1a;程序的每个字母逻辑浏览一遍 五&#xff1a;重启电脑 六&#xff1a;环境软件重装 七&#xff1a;回去睡一觉&#xff0c;等待明天早上再看 八…

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

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

总结libevent安装方法

1.先用&#xff1a;ls -al /usr/lib | grep libevent 查看是否已安装&#xff0c;如果已安装且版本低于1.3&#xff0c;则先通过&#xff1a;rpm -e libevent —nodeps 进行卸载。 2.下载libevent安装包&#xff1a;libevent-1.4.13-stable.tar.gz&#xff0c;然后解压。 3.切…

opencv中匹配点对的坐标提取

在opencv中&#xff0c;特征检测、描述、匹配都有集成的函数。vector<DMatch> bestMatches;用来存储得到的匹配点对。那么如何提取出其中的坐标呢&#xff1f; int index1, index2;for (int i 0; i < bestMatches.size(); i)//将匹配的特征点坐标赋给point{index1 b…

echarts 弹出放大_Echarts图标增加全屏/放大功能

需求描述&#xff1a;客户爸爸在看某个echarts图表时&#xff0c;图标的数据很多&#xff0c;字看着很小&#xff0c;客户爸爸希望能双击放大某个图表。如下面这个图表字太小&#xff0c;太密。image.png解决方式&#xff1a;网上有不少方式是用Echarts的tool工具先上自定义工具…

Hdoj 1847.Good Luck in CET-4 Everybody! 题解

Problem Description 大学英语四级考试就要来临了&#xff0c;你是不是在紧张的复习&#xff1f;也许紧张得连短学期的ACM都没工夫练习了&#xff0c;反正我知道的Kiki和Cici都是如此。当然&#xff0c;作为在考场浸润了十几载的当代大学生&#xff0c;Kiki和Cici更懂得考前的放…

mexw32与mexw64

关于mex文件 MATLAB的mex文件是一种特征的函数封装形式&#xff0c;这类函数一般由C/C语言编写的&#xff0c;经过MATLAB编译器处理而生成的二进制文件。它可以被MATLAB解释器自动装载并执行的动态链接程序&#xff0c;类似于windows下的dll文件。在使用的时候也可以像普通的.m…

对外汉语语料库有哪些_国内语料库建设一览表

英语学习者语料库(书面语及口语)中国学习者语料库 CLEC(100万)广外、上海交大大学英语学习者口语语料库 COLSEC (5万) 上海交大香港科技大学学习者语料库 HKUST Learner Corpus 香港科技大学中国英语专业语料库 CEME (148万) 南京大学中国英语学习者口语语料库 SECCL (100万) 南…

数据库设计(一对一、一对多、多对多)

关联映射&#xff1a;一对多/多对一 存在最普遍的映射关系&#xff0c;简单来讲就如球员与球队的关系&#xff1b; 一对多&#xff1a;从球队角度来说一个球队拥有多个球员 即为一对多 多对一&#xff1a;从球员角度来说多个球员属于一个球队 即为多对一数据表间一对多关系如下…

oozie与hue整合 执行WC案例报错: 连接10020端口被拒绝

Call From hdp-05/192.168.230.15 to hdp-01:10020 failed on connection exception: java.net.ConnectException: 拒绝连接; For more details see: http://wiki.apache.org/hadoop/ConnectionRefused错误原因没有启动hadoop的history程序JobHistoryServer进程监听端口就是10…