可怕的DefaultAbstractHelperImpl

不久前,我们发布了这个有趣的游戏,我们称之为Spring API Bingo 。 当形成有意义的类名时,它是对Spring的巨大创造力的赞美和奉承。

  • FactoryAdvisorAdapterHandlerLoader
  • ContainerPreTranslatorInfoDisposable
  • BeanFactoryDe​​stinationResolver
  • LocalPersistenceManagerFactoryBean

上面的两个类实际上存在。 你能发现他们吗? 如果没有,请玩Spring API Bingo !

显然,Spring API遭受...

命名事物

在计算机科学中只有两个难题。 缓存失效,命名和错误一处

– Tim Bray引用Phil Karlton

在Java软件中很难摆脱其中的几个前缀或后缀。 考虑一下最近在Twitter上的讨论,这不可避免地导致了(非常)有趣的讨论 :

是的, Impl后缀是一个有趣的话题。 我们为什么要拥有它,为什么我们要一直这样命名呢?

规格与主体

Java是一种古怪的语言。 在其发明之初,面向对象是一个热门话题。 但是过程语言也具有有趣的功能。 当时,一种非常有趣的语言是Ada (以及主要来自Ada的PL / SQL)。 Ada(如PL / SQL)在包中合理地组织了过程和功能,有两种形式:规范和主体。 从维基百科示例 :

-- Specification
package Example isprocedure Print_and_Increment (j: in out Number);
end Example;-- Body
package body Example isprocedure Print_and_Increment (j: in out Number) isbegin-- [...]end Print_and_Increment;begin-- [...]
end Example;

您始终必须这样做,并且两件事的名称完全相同: Example 。 它们存储在两个不同的文件中,分别称为Example.ads (ad用于表示Ada,s用于说明)和Example.adb (用于主体)。 PL / SQL遵循该命令,并使用pk作为Package来命名包文件Example.pksExample.pkb

Java采取了不同的方式,主要是因为多态性和类的工作方式:

  • 类既是规范又是主体
  • 接口的名称不能与实现类的名称相同(大多数情况下,当然有很多实现)

特别是,类可以是纯规范的混合体,具有部分主体(抽象时)和完整规范和主体(具体时)。

这如何转换为Java命名

并非所有人都对规范和机身的清晰分离感到赞赏,这当然可以争论。 但是,当您处于那种具有Ada风格的思维方式时,那么您可能想要为每个类(至少在公开API的任何地方)都使用一个接口。 我们对jOOQ进行了相同的操作 ,在其中我们建立了以下策略来命名事物:

* Impl

与相应接口成1:1关系的所有实现(主体)都以Impl为后缀。 如果可能的话,我们尝试将那些实现保留为私有包,并因此密封在org.jooq.impl包中。 例如:

  • Cursor及其对应的CursorImpl
  • DAO及其对应的DAOImpl
  • Record及其对应的RecordImpl

这种严格的命名方案可以立即清楚地表明,哪个是接口(因此是公共API),哪个是实现。 从这个方面来说,我们希望Java更像Ada,但是我们拥有出色的多态性,并且…

抽象*

…并导致在基类中重用代码。 众所周知,常见的基类应该(几乎)总是抽象的。 仅仅是因为它们最经常是其相应规范的不完整实现(实体)。 因此,我们有很多局部实现,它们也与对应的接口处于1:1关系,并为它们加上Abstract前缀。 通常,这些部分实现也是私有包的,并​​密封在org.jooq.impl包中。 例如:

  • Field及其对应的AbstractField
  • Query及其对应的AbstractQuery
  • ResultQuery及其对应的AbstractResultQuery

特别地, ResultQuery扩展 Query的接口,因此AbstractResultQuery扩展 AbstractQuery的部分实现,后者也是部分实现。

在我们的API中完全实现部分实现是很有意义的,因为我们的API是内部DSL(特定于域的语言) ,因此无论具体Field实际Substring如何,都有成千上万种始终相同的方法,例如Substring

默认*

我们做与接口相关的所有API。 在流行的Java SE API中,已经证明这种方法非常有效,例如:

  • 馆藏
  • JDBC
  • DOM

我们还做与接口相关的所有SPI(服务提供商接口) 。 就API的发展而言,API和SPI之间存在一个本质区别:

  • API是由用户消费 ,难以实现
  • SPI由用户实现 ,几乎不消耗

如果您不开发JDK(因此没有完全疯狂的向后兼容规则 ),则通常可以安全地向API接口添加新方法。 实际上,我们在每个次要版本中都这样做,因为我们不希望任何人实现我们的DSL(谁想要实现Field的 286方法或DSL的 677方法。这太疯狂了!)

但是SPI是不同的。 每当您为用户提供SPI(例如带有*Listener*Provider后缀)时,您都不能简单地向他们添加新方法-至少不是在Java 8之前,因为这会破坏实现,并且其中有很多。

好。 我们仍然这样做,因为我们没有那些JDK向后兼容规则。 我们有更多的放松 。 但是我们建议用户不要直接自己实现接口,而应扩展一个Default实现,该实现为空。 例如ExecuteListener和对应的DefaultExecuteListener

public interface ExecuteListener {void start(ExecuteContext ctx);void renderStart(ExecuteContext ctx);// [...]
}public class DefaultExecuteListener
implements ExecuteListener {@Overridepublic void start(ExecuteContext ctx) {}@Overridepublic void renderStart(ExecuteContext ctx) {}// [...]
}

因此, Default*是一个前缀,通常用于提供API使用者可以使用和实例化或SPI实现者可以扩展的单个公共实现,而不会冒向后兼容的风险。 对于Java 6/7缺少接口默认方法,这几乎是一种解决方法,这就是为什么前缀命名更加合适的原因。

此规则的Java 8版本

实际上,这种做法表明,指定Java-8兼容SPI的“好”规则是使用接口,并使所有方法默认为空。 如果jOOQ不支持Java 6,我们可能会这样指定ExecuteListener

public interface ExecuteListener {default void start(ExecuteContext ctx) {}default void renderStart(ExecuteContext ctx) {}// [...]
}

*实用程序或*助手

好的,这里是供模拟/测试/覆盖专家和爱好者使用的工具。

为各种静态实用程序方法进行“转储”是完全可以的 。 我的意思是, 您当然可以成为面向对象的警察的成员 。 但…

请。 不要成为“那个家伙”!

因此,有多种识别实用程序类的技术。 理想情况下,您应遵循命名约定,然后再遵守。 例如* Utils

从我们的角度来看,理想情况下,您甚至只转储所有未严格绑定到单个类中特定领域的实用程序方法,因为坦率地说,您最后一次欣赏何时必须经历数百万个类才能找到该实用程序方法? 决不。 我们有org.jooq.impl.Utils 。 为什么? 因为它将允许您执行以下操作:

import static org.jooq.impl.Utils.*;

这样,几乎感觉就像您在整个应用程序中都具有“顶级功能”之类的东西。 “全局”功能。 我们认为这是一件好事。 而且我们完全不赞成“我们不能嘲笑这一论点”, 因此甚至不要尝试开始讨论。

讨论区

…或者实际上,让我们开始讨论。 您的技术是什么,为什么? 以下是汤姆·布约克(Tom Bujok)原始Tweet的一些反应,可帮助您入门:

我们走吧 !

翻译自: https://www.javacodegeeks.com/2014/10/the-dreaded-defaultabstracthelperimpl.html

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

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

相关文章

微信小程序 监听位置信息

wx.onLocationChange(function callback) | 微信开放文档微信开发者平台文档https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.onLocationChange.html 小程序 获取当前城市位置-高德地图_Start2019-CSDN博客小程序获取位置信息,包括省市区、用户…

通过构建Cocoapods私有库进行组件化开发探索

专题一 一、创建私有索引库 选Github或者码云都可以,本例以Github为例。创建私有索引库用来作为自己组件库的索引: 二、本地添加私有索引库 添加:pod repo add 索引库名称 索引库地址 例:pod repo add ZYHModule https://github.c…

lodash 数组元素查找 findIndex

_.findIndex(array, [predicate_.identity])这个方法类似 _.find。除了它返回最先通过 predicate 判断为真值的元素的 index &#xff0c;而不是元素本身。<!DOCTYPE html><html lang"zh"><head><meta charset"UTF-8" /><meta …

layui 父页面弹框中获取子页面的内容

layer弹层组件开发文档 - Layuihttps://www.layui.site/doc/modules/layer.html#layer.getChildFrame 需求及代码分析 如上图&#xff0c;原来是点击一个按钮跳转到子页面&#xff0c;现在想改为&#xff1a;点击按钮&#xff0c;在当前页打开。因为不想要重写子页面的样式及内…

在JBoss Fuse / Fabric8 / Karaf中使用Byteman

您是否曾经尝试过了解一些非常简单的方法不起作用的过程&#xff1f; 您正在任何众所周知的上下文中编写代码&#xff0c;无论出于何种原因它都无法正常工作。 而且您信任您的平台&#xff0c;因此您认真阅读了所有日志。 而且&#xff0c;您仍然不知道为什么某些行为不符合预…

个人作业——Alpha项目测试

一、 这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/ 这个作业要求在哪里 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/3338 团队名称 西柚皇家编程团队&#xff1a;https://www.cnblogs.com/TakeRa…

HttpURLConnection的警告

这段代码对您来说还不错吗&#xff1f; HttpURLConnection connection null; try {connection (HttpURLConnection) url.openConnection();try (InputStream in url.getInputStream()) {return streamToString(in);} } finally {if (connection ! null) connection.disconn…

LeapFTP 注册码

LeapFTP v2.7.6.613 汉化版II 特别版Name:crsky Code:214065-658136565http://www.crsky.com/soft/664.htmlLeapFTP v2.7.6.613 英文版http://www.crsky.com/soft/378.htmlName:crsky Code:214065-658136565------------LeapFTP 2.7.6.613 汉化版软件类别&#xff1a;汉化补丁/…

Windows7下Docker的安装

转自 https://blog.csdn.net/xiangxiezhuren/article/details/79698913 无法打开图3&#xff0c;打开属性。给其添加git路径 无法使用图2下载 https://www.cnblogs.com/huang-yc/p/10350408.html 转载于:https://www.cnblogs.com/shufeiyang/p/11016177.html

简而言之,JUnit:单元测试断言

简而言之&#xff0c;本章涵盖了各种单元测试断言技术。 它详细说明了内置机制&#xff0c; Hamcrest匹配器和AssertJ断言的优缺点 。 正在进行的示例扩大了该主题&#xff0c;并说明了如何创建和使用自定义匹配器/断言。 单元测试断言 信任但要验证 罗纳德里根&#xff08;R…

vue 安装使用mockjs

使用 mockjs 的案例过程&#xff1a; 1. 安装 npm install axios -S npm install mockjs --save-dev npm install --save 、--save-dev 、-D、-S 的区别与NODE_ENV的配置_jwl_willon的博客-CSDN博客_npm save备注&#xff1a;<> 意为等价于&#xff1b;1、npm install …

vue 总结

1、尽量不操作dom 使用数据双向绑定 2、使用指令 如:title 不是title 更多专业前端知识&#xff0c;请上 【猿2048】www.mk2048.com

OpenFileDialog 类的ShowDialog() 错误的解决

首先&#xff0c;一个类里&#xff0c;有个linkLabel1 private OpenFileDialog openFileDialog1;private DialogResult result; private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { openFileDialog1 new OpenFileDialog(); …

大数据学习——SparkStreaming整合Kafka完成网站点击流实时统计

1.安装并配置zk 2.安装并配置Kafka 3.启动zk 4.启动Kafka 5.创建topic [rootmini3 kafka]# bin/kafka-console-producer.sh --broker-list mini1:9092 --topic cyf-test 程序代码 package org.apache.sparkimport java.net.InetSocketAddressimport org.apache.spark.HashParti…

日期/时间格式/解析,Java 8样式

自Java 几乎 开始以来&#xff0c;Java开发人员就通过java.util.Date类&#xff08;自JDK 1.0起&#xff09;和java.util.Calendar类&#xff08;自JDK 1.1起 &#xff09;来处理日期和时间。 在这段时间内&#xff0c;成千上万&#xff08;甚至可能数百万&#xff09;的Java开…

vue-router 响应路由参数的变化

提醒一下&#xff0c;当使用路由参数时&#xff0c;例如从 /user/foo 导航到 /user/bar&#xff0c;原来的组件实例会被复用。因为两个路由都渲染同个组件&#xff0c;比起销毁再创建&#xff0c;复用则显得更加高效。不过&#xff0c; 这也意味着组件的生命周期钩子不会再被…

无废话WPF系列5:控件派生图

1. WPF类控件的派生关系图&#xff0c;紫色的部分开始才算是进入WPF的框架里。 2. WPF控件图 WPF的UI控件主要有以下类型&#xff0c;ContentControl, HeaderedContentControl, ItemsControl, HeaderedItemsControl, Panel, Adorner(文字点缀元素), Flow Text(流式文本元素), T…

安装ipython和jupyter

本节内容&#xff1b; 安装ipython安装jupyterPycharm介绍 Python软件包管理一、安装ipython 1. python的交互式环境2. 安装ipython 可以使用pip命令安装。如果你是用pyenv安装的python的话&#xff0c;pip命令已经有了。 当需要安装包的时候&#xff0c;最好进入虚拟环境&…

vue 图片资源应该如何存放并引入(public、assets)?

全局cli配置&#xff1a;vue.config.js 之前写项目就想着怎么简单怎么来&#xff0c;图片要用了&#xff0c;就直接在要用图片的页面&#xff0c;新建一个跟页面同等级的 imgs 文件夹&#xff0c;然后在页面中直接 “./imgs/图片.png”&#xff0c;不得不说这样写很方便。 但是…

真正的动态声明性组件

在这篇简短的文章中&#xff0c;我将重点介绍ADF动态声明性组件。 我的意思是一个众所周知的ADF标签af&#xff1a;declarativeComponent 。 它可以用作将页面设计为页面片段和组件组成的一种非常便捷的方法。 例如&#xff0c;我们的页面可以包含以下代码片段&#xff1a; &l…