关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java命名:可怕的DefaultAbstractHelperImpl

    Java命名:可怕的DefaultAbstractHelperImpl

    2014-12-29 00:00:00 出处:oschina
    分享

    JOOQ的卢卡斯·艾德 研究了在Spring和Java命名策略中富有创造性的类名所带来的价值。

    这篇文章最早是发表在jooq.org上,作为聚焦于jOOQ上所有关于Java、SQL以及软件开发的系列的一部分。

    前段时间,我们发布了这款被我们称作Spring API Bingo的趣味游戏。这是对Spring构造类名时展现出的创造性极尽赞美。类名如下:

    FactoryAdvisorAdapterHandlerLoader ContainerPreTranslatorInfoDisposable BeanFactoryDestinationResolver LocalPersistenceManagerFactoryBean

    其中有两个是实际存在的,你能指出来吗?如果不能,来玩下Spring API Bingo吧!

    很明显,Spring API也不得不面对下列问题……

    命名

    “计算机科学中只有两个困难的问题。缓存失效,命名,以及差一错误” –Tim Bray quoting Phil Karlton

    在Java代码中有几对前缀和后缀非常难以摆脱。考虑到最近在推特上的讨论,前缀和后缀问题不可避免的引发了非常有趣的争论。

    使用接口:PaymentServce的实现:PaymentServiceImpl ,它的测试应该命名为PaymentServiceImplTest而不是PaymentServiceTest。

    ——Tom Bujok (@tombujok) 2014年10月8日

    是的,Impl后缀是一个有趣的主题。我们为什么要使用它,以及我们为什么要继续这样命名?

    说明 vs 主体

    Java是一个古怪的语言。Java面世的时候,面向对象是最热门的话题。但是过程式语言也有一些有趣的特性。当时一个非常有趣的语言叫做Ada(以及大部分源于Ada的 PL/SQL)。Ada(像PL/SQL)在包里合理的组织了一些过程和方法,由此产生了两种类型:说明(specification)和 主体(body)。来自维基百科的例子:

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

    你必须要这么做,并且这两个都要命名为Example。这两部分要存到两个不同的文件中,一个叫做 Example.ads(ad出自于Ada,s出自于specification),另一个叫做Example.adb(b来自body)。PL/SQL模仿这一规范,给package文件命名为Example.pks和Example.pkb,pk源自Package。Java走了一条不同的路,主要是因为多态和类的运行方式:

    类既是说明也是主体,接口不能和实现类用同样的名字(当然主要是因为有多个实现类)。 特别的,类可以分为仅说明,有部分主体的(当它们是抽象类时)以及说明和主体都有的(具体类)这几类。

    如何将这些转化为Java中的命名

    不是所有人都喜欢这种把说明和主体清楚地分开,这个显然会有争论。但是当你在Ada式的思维中,你很可能会希望一个接口给所有类使用,至少也应该给API暴露出来的类使用。我们在JOOQ也是这样做的,在JOOQ,我们制定了如下策略来命名:

    *Impl

    所有的和接口(interface)有一对一关系的实现类(主体)都以Impl作为后缀。如果可以的话,尽量把这些实现放在包里,这样就可以封闭在包org.jooq.impl里了。例如:

    Cursor接口和它对应的实现CursorImpl。 DAO接口和它对应的实现DAOImpl。 Record和它的对应实现RecordImpl。

    这种严格的命名模式使得哪个是接口,哪个是实现变得直接且清晰。我们希望Java在这方面可以更像Ada,但是我们有更好的多态,以及……

    Abstract*

    ……使得在基类中对代码进行重用。 正如我们知道的,公共的基类应该(几乎全部)总是抽象的。因为它们大部分情况下都没有完全实现和它们对应的说明。因此, 我们有很多和接口一一对应的实现类,我们给这些部分实现类加上前缀Abastract。多数情况下,这些部分实现的类也是在包内,然后封装在org.jooq.impl包里。

    例如:

    Field以及它的对应抽象类AbstractField。 Query以及它的对应抽象类AbstractQuery。 ResultQuery以及它的对应抽象类AbstractResultQuery。

    特别的,ResultQuery是一个继承Query的接口,thusAbstractResultQuery是一个继承了theAbstractQuery并实现了部分方法的抽象类,而theAbstractQuery也是一个实现了部分方法的抽象类。拥有部分实现让我们的 API更加合理,因为我们的API是一个内部的DSL(Domain-Specific Language领域特定语言)。因此,不管Fieldreally的实现是什么,都有成千上万的相同方法,例如Substring。

    Default*

    我们使用接口来做任何与API相关的事情。这在Java SE API中已经证明这是很有效的,例如:

    Collections Streams JDBC DOM

    我们也使用接口来做任何与SPI(服务提供接口)相关的事情。API和SPI之间有一个很重要的区别,按照API的演化:API由用户来使用而不实现,SPI由用户实现但不使用。如果你不开发JDK(因此不用理会那些令人发疯的向后兼容规则),你基本上可以很安全的在API接口中添加新方法。

    事 实上,我们在比较小的版本发布时会这样做。因为我们没有期望任何人去实现我们的DSL(谁会想要实现Field的286个方法,或者DSL的677个方 法,简直是疯了)。但是SPI不同。无论什么时候只要向你的用户提供了SPI,你就不能简单地增加新方法——至少在Java8之前不能增加,因为那样会破坏实现类,并且这样的实现类有很多。

    然后,我们仍然这样做,因为我们没有像JDK那样的向后兼容的规则。我们的规则更加轻松。但是我们仍不建议用户直接自己实现接口,但是可以继承一个空的Defaultimplementation。例如ExecuteListener以及它的对应空实现DefaultExecuteListener:

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

    所以,通常Default*是一个用来为API用户提供的,可以使用并且实例化的公共实现类的前缀。或者用作SPI实现可以继承的前缀(不用冒着向后兼容的风险)。这差不多是Java 6、Java 7′缺乏接口默认方法的临时解决办法,这也是为什么命名前缀更加合适的原因。

    这条规则的Java 8的版本

    事实上,这个实践证明定义Java 8兼容SPI一个好用的规则是使用接口并且使所有方法都有一个默认的空方法体。如果JOOQ不支持Java 6,我们很可能像下面这个定义我们的ExecuteListener:

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

    *Utils 或者 *Helper

    好的,这个是为mock、testing、coverage专家和狂热爱好者准备的。有个“垃圾站”来放置所有种类的静态工具方法当然是可以的。我的意思是,你当然可以成为面向对象“警察”中的一员。但是

    请不要成为那样的家伙。

    那 么,有各种给工具类命名的技术。理想情况下,你使用一个命名约定,然后一直使用它。例如 *Utils。我们的观点是,理想情况下你甚至可以把所有的没有和某个域有严格关联的工具方法放到一个类里面。坦白说,你上次不得不在上百万的类中去找你需要的工具方法是什么时候?从不。我们有org.jooq.impl.Utils。为什么?因为我们可以这样做:

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

    你几乎拥有贯穿整个程序的“顶层方法”一样的东西。我们觉得“全局”方法也是很好的东西。我们是绝不买“我们没办法mock”之类讨论的账,所以不要试图挑起争论。

    讨论

    或者,事实上,让我们掀起一场讨论。你的技巧是什么,为什么?下面是对Tom BujokTweet原文的回应,来帮助你开始这场争论:

    @tombujok 不。PaymentServiceImplTestImpl! — Konrad Malawski (@ktosopl) October 8, 2014

    @tombujok 放弃使用接口

    — Simon Martinelli (@simas_ch) October 8, 2014

    @tombujok 给所有的东西填上Impl后缀。

    — Bartosz Majsak (@majson) October 8, 2014

    @tombujok @lukaseder @ktosopl 根原因是类不应该被叫做 *Impl,但是我知道你们一直故意挑起我们的争论。 — Peter Kofler (@codecopkofler) October 9, 2014

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。