使用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吗?相关推荐

  1. instanceof_您真的需要instanceof吗?

    instanceof 使用instanceof是一种代码味道. 我认为我们可能对此表示同意. 每当我看到这样的构造时,我都会确定出现了问题. 也许有人只是在进行更改时没有注意到问题? 也许有一个主意, ...

  2. JavaScript instanceof 运算符深入剖析

    简介: 随着 web 的发展,越来越多的产品功能都放在前端进行实现,增强用户体验.而前端开发的主要语言则是 JavaScript.学好 JavaScript 对开发前端应用已经越来越重要.在开发复杂产 ...

  3. JavaScript instanceof 运算符深入剖析【转载】

    http://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/ instanceof 运算符简介 在 JavaScript 中, ...

  4. jep290涉及jdk版本_JDK 14 / JEP 305模式匹配“ Smart Casts”实例

    jep290涉及jdk版本 我通常将Java代码中instanceof运算符的存在视为" 红色标志 ",这意味着在某些情况下使用instanceof不一定是错误的,但是使用它有时表 ...

  5. JDK 14 / JEP 305模式匹配“ Smart Casts”实例

    我通常将Java代码中instanceof运算符的存在视为" 红色标志 ",这意味着在某些情况下使用instanceof不一定是错误的,但是使用它有时表示可以以一种更干净的方式解决 ...

  6. 好家伙!JDK16 GA 终于发布,内置 Lombok 的功能,真的顶?

    就在昨天,JDK16 GA正式发布: 2020/12/10  Rampdown Phase One (fork from main line) 2021/01/14  Rampdown Phase T ...

  7. 你的接口,真的能承受高并发吗?

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 作者:肥朝 出处:公众号[肥朝] 前言 本篇主要讲解的是前阵子的一个压测问题.那么就直接开门见山 ...

  8. java js关键字_js中的instanceof关键字

    instanceof是Java的一个二元操作符,和==,>, String s = "I AM an Object!"; boolean isObject = s insta ...

  9. Java中的instanceof关键字

    Java中的instanceof关键字 instanceof是Java的一个二元操作符,和==,>,<是同一类东东.由于它是由字母组成的,所以也是Java的保留关键字.它的作用是测试它左边 ...

最新文章

  1. Ant Design Pro 使用图表 charts bizcharts
  2. (转) Docker EE/Docker CE简介与版本规划
  3. NDCG:推荐系统/搜索评价指标
  4. VTK修炼之道10:可视化管道的连接与执行
  5. python 查看当前目录_Python的武器库11:os模块
  6. requestbody前端怎么传_学习前端开发前的基础知识了解「V1001」
  7. 思维导图网页版、临时使用推荐工具——画图本
  8. jQuery技术内幕:深入解析jQuery架构设计与实现原理1
  9. 智汇华云 | 集群自动化部署服务流程之自动化集群配置(上)
  10. android进阶2之有道词典开发,Android进阶2之有道词典开发
  11. Python3使用代理爬取某网文献摘要(完整源码)
  12. java实现excel转pdf_java实现excel转pdf(poi+itext)
  13. 微信公众平台开发实战Java版之如何网页授权获取用户基本信息
  14. 软件开发学习的5大技巧,你知道吗?
  15. 微信防封汇总,解决办法及数据分析
  16. 基于神经网络的车牌识别,卷积神经网络车牌识别
  17. Beyond Compare 中文乱码解决
  18. ISO 16750.4-2010道路车辆电子电气部件的环境试验 第四部分
  19. vscode open in browser插件失效
  20. 简易音乐播放器(js,html,css实现)

热门文章

  1. Go语言、Docker 和新技术
  2. 深入并发包-ConcurrentHashMap
  3. 统一配置中心的设计方案
  4. 简单介绍Java中Comparable和Comparator
  5. Java 高并发下的实践
  6. Vs Code如何自定义设置一键代码补全
  7. 搭建ssh框架的步骤
  8. CentOS - 修改主机名教程(将 localhost.localdomain 改成其它名字)
  9. 在实际使用中 mysql所支持的触发器有_2016计算机二级MySQL冲刺题及答案
  10. java中两个map的融合(两个map有相同字段)