使用默认方法进行接口演化–第二部分:接口

引入了默认方法以启用接口演进。 如果向后兼容是不可替代的,则仅限于向接口添加新方法(这是它们在JDK中的唯一用法)。 但是,如果希望客户端更新其代码,则可以使用默认方法逐步演化接口而不会引起编译错误,从而使客户端有时间将其代码更新为新版本的接口。

这个小型系列的第一部分说明了默认实现如何允许在不破坏客户端代码的情况下添加,替换和删除方法。 我愚蠢地宣布:“以后的文章将探讨替换整个接口的方法”,同时也不会破坏客户端代码。

好吧,您现在正在阅读这篇文章,不幸的摘要是:

我无法使其工作。

为什么? 泛型。

到底为什么 你真的想知道吗 好吧,那么请继续阅读,但是这篇文章的其余部分实际上只是对我如何成为障碍的描述,因此不要期望太多。 (大激励,是吗?)

总览

首先,在描述我尝试过的方法和失败的方法之前,先定义要解决的问题。

问题陈述

这是我们想要做的:

假设您的代码库包含一个接口,您的客户端可以用所有可以想象的方式使用该接口:它们具有自己的实现,使用其实例调用您的代码,并且您的代码返回此类实例,当然他们将其用作参数的类型和返回值。

现在,您要实质性地更改接口:以无法用对单个方法的更改来表示的方式对其进行重命名,移动或修改。 (但是从提供一个版本到另一个版本的角度来看,这两个接口仍然是等效的。)

您可以这样做,发布具有更改的新版本,并告诉您的客户端修复其导致的编译错误。 如果他们的代码与您的代码高度耦合,那么他们可能必须在单独的分支中执行此操作以花一些时间在代码上,但这就是生活,对吗? 不过,您真是个好人,因此,与其要求费时的日子,不如让他们有机会随着时间的推移(例如,直到下一个版本)逐渐更改其代码,而没有任何编译错误。

(请注意,这是接下来所有内容的主要要求。我首先忽略了这是否是个好主意。我只是想看看能达到的程度。)

我认为甚至有机会实现这一目标的唯一方法是定义一个过渡阶段,在该阶段中,新旧版本的接口都将共存。 因此,我们真正需要的是一种通用的分步方法,即如何将实现,调用者和声明从一个接口转移到另一个接口。

想法

在宣布这篇文章时,我对它的工作方式有一个具体的想法。 基本上与我在方法中使用的方法相同。

不断发展的接口方法

使用默认方法添加,替换或删除接口的单个​​方法非常简单,通常包括三个步骤(在某些情况下更少):

  • 新版本:库的新版本发布,其中界面定义是过渡性的,并结合了旧的和新的所需轮廓。 默认方法可确保所有外部实现和调用仍然有效,并且在更新时不会出现编译错误。
  • 过渡:然后客户有时间从旧大纲过渡到新大纲。 同样,默认方法可确保适应的外部实现和调用有效,并且可以进行更改而不会编译错误。
  • 新版本:在新版本中,该库删除了旧轮廓的残差。 鉴于客户端明智地利用了自己的时间并进行了必要的更改,因此发布新版本不会导致编译错误。

如果您对这些步骤的详细说明感兴趣,可以阅读我的早期文章 。

改进界面

在这种情况下,这种方法似乎也很有意义,所以我坐下来进行演示。

如果整个接口发生更改,则要复杂一些,因为在方法仅具有调用者和实现的地方,该接口也是一种类型,即可以在声明中使用。 这使得必须区分三种使用接口的方式:

  • 内部使用 ,您在其中拥有实现和使用接口的代码
  • 已发布的使用 ,您拥有实现,但客户端调用了代码
  • 外部使用 ,其中客户端拥有实现和使用接口的代码

起作用的部分采用与演化方法相同的方法:

  • 新版本:使用新界面发布新版本,以扩展旧版本。 让所有内部代码实现并使用新接口。 所有已发布的代码将使用旧接口声明参数类型,并使用新接口返回类型。 如果必须转换实例,则可以使用适配器来完成。 现在忽略参数化类型,此更改将不会导致客户端代码中的编译错误。
  • 过渡:版本发布后,客户端更改其代码。 从旧接口的实现(更改为实现新接口的实现)和已发布的代码返回的实例开始,他们可以开始声明新类型的实例,更新将它们传递给它们的方法的参数类型,等等。上。 如有必要,可以暂时使用适配器通过新接口与旧实例进行交互。
  • 新版本:发布一个删除旧界面的版本。

与不断发展的方法相同,新接口中的默认实现允许客户端代码停止明确实现旧接口,从而可以在第二个版本中将其删除。 另外,旧接口上的便捷asNew()方法可以调用适配器以使其自身适应新接口。

我掩盖了一些细节,但我希望你相信我,这是可行的。 现在让我们回到泛型…

障碍

提出的方法中的关键部分是已发布的代码。 它由您的客​​户调用,因此第一个发行版必须以兼容的方式对其进行更改。 并且由于所有内部代码都需要新接口,因此它必须迈出从OldNew的一步。

没有泛型,它可能看起来像这样:

在已发布的代码中将“旧”转换为“新”

// in version 0
public Old doSomething(Old o) {// 'callToInternalCode' requires an 'Old'callToInternalCode(o);return o;
}// in version 1 the method still accepts 'Old' but returns 'New'
public New doSomething(Old o) {// 'callToInternalCode' now requires a 'New'New n = o.asNew();callToInternalCode(n);return n;
}

好的,到目前为止很好。 现在,让我们看看泛型的外观。

在已发布的代码中将“旧”转换为“新” –泛型

// in version 0
public Container<Old> doSomething(Container<Old> o) {// 'callToInternalCode' requires a 'Container<Old>'callToInternalCode(o);return o;
}// in version 1
// doesn't work because it breaks assignments of the return value
public Container<New> doSomething(Container<Old> o) {// 'callToInternalCode' requires a 'Container<New>'// but we can not hand an adapted version to 'callToInternalCode'// instead we must create a new containerNew nInstance = o.get().asNew();Container<New> n = Container.of(nInstance);callToInternalCode(n);return n;
}

因此,使用已发布的代码层从旧界面适应新界面通常不起作用,原因至少有两个:

  • 由于Java中泛型的不变性,返回值的所有分配都将中断:

    不变性打破分配
    Container<Old> old = // ...
    // works in version 0; breaks in version 1
    Container<Old> o = published.doSomething(old);
  • 不能将同一Container实例从已发布传递到内部代码。 这导致两个问题:
    • 创建一个新容器可能很困难或不可能。

该死的…

由华盛顿州交通运输部根据CC-BY-NC-ND 2.0发布。

发布时间由交通运输的华盛顿州部门在CC-BY-NC-ND 2.0 。

从一开始,我就感到仿制药会很麻烦-回想起来,这实际上很明显。 当涉及类型时,泛型怎么可能不是问题。 因此,也许我应该先尝试解决难题。

可能绕行

在将我的头撞在墙上一段时间之后,我仍然没有找到解决此问题的通用方法。 但是我想出了一些可能有助于解决特殊情况的想法。

通配符

您可以检查已发布的内部代码是否充分利用了通配符(请记住PECS )。 您也可以建议客户如何使用它们。

根据情况,这可能会产生解决方案。

专用接口,类,实例

根据具体的代码,可以提供使用旧接口的已发布接口,类或实例的新版本。 如果可以通过让客户端选择使用依赖于旧接口的接口,类或实例还是依赖于新接口的接口,类或实例的方式来处理代码,则各个实现不必进行过渡。

但这可能会将旧界面推回内部代码,而内部代码刚刚更新为仅使用新接口。 听起来也不好。

容器适配器

您可以在已发布的代码中为与旧接口一起使用的容器提供适配器。 这实际上将允许您在这些容器上调用asNew()

(出于不相关的原因,我目前正在为某些JDK集合进行此类转换。下一个版本的LibFX将包含它们;如果您好奇,可以在GitHub上查看演示。)

算了!

这一切又是为了什么? 为了防止客户创建分支,在将所有内容合并回master之前花一些时间在那里修复问题吗? 算了!

在这一点上,这是我对此的看法。 只要您只处理单个方法,接口的演化就很平稳,但是当您要替换整个接口时,这似乎会很痛苦。 因此,除非有充分的理由介绍所有这些复杂性,否则我将以困难的方式进行操作,然后让客户对其进行分类。 还是根本不做。

而且,如果您只是重命名或移动界面,无论如何,大部分甚至全部工作都可以通过简单的搜索替换来完成。

反射

我们重申了如何将默认方法用于发布,过渡和发布三部分的界面演化。 尽管这对单个方法有效,但我们发现它无法替换整个接口。 主要问题是参数类型的不变性使我们无法将已发布的代码用作适应层。

即使我们看到了一些解决该问题的方法,也没有一个好的解决方案脱颖而出。 最后,看起来不值得麻烦。

我有事吗 还是整个想法愚蠢? 为什么不发表评论!

翻译自: https://www.javacodegeeks.com/2015/04/interface-evolution-with-default-methods-part-ii-interfaces.html

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

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

相关文章

【APICloud系列|37】百度开放平台应用+,提升用户下载量操作步骤

进入百度应用开放平台后台&#xff0c;按图中步骤点击。 接入步骤 这个首先要准备和应用名称一致的百家号 链接地址&#xff1a; 进入官方号平台&#xff1a;http://gfh.baidu.com/ 填写官方的ID以及联系人的手机号即可&#xff0c;最后点击提交。

linux 命令 抛后台,Linux 后台执行命令

场景python 代码&#xff0c;打印1~3000&#xff0c;每秒打印一次## file_name: test.pyimport timei 0while 1:time.sleep(1)i i 1print(i)if i > 3000:break问题&#xff1a;直接在终端执行&#xff1a;python test.py, 需要在这个终端一直等&#xff0c;没法干别的事了…

【JSP】JSP与oracle数据库交互案例

************************************************************************ ****原文&#xff1a;blog.csdn.net/clark_xu 徐长亮的专栏 ************************************************************************ 本案例为咖啡销售情况录入查询系统 一、数据输入系统&…

【APICloud系列|34】上架华为应用市场缺少免责函?

针对华为应用市场&#xff0c;不管提交的是什么类型的应用&#xff0c;都应该提交免责函&#xff0c; 需要提前下载模板进行盖章后&#xff0c;转成图片上传。 免责函模板如下&#xff1a; 被拒绝的信息如下&#xff1a; 再次将免责函提交上去就没啥问题啦&#xff0c;因为其…

Property Animator 属性动画概述与示例

Property Animation 属性动画 属性动画是一个强大的框架&#xff0c;它几乎可以让你的所有东西做动画&#xff0c;你可以随着时间的推移通过更改他们的属性来定义动画&#xff0c;无论它是否绘制在屏幕上&#xff0c;一个属性动画是在指定的时间内去改变属性&#xff08;一个对…

注册服务号、订阅号流程

进入微信公众平台,选择注册类型,订阅号/服务号 填写基本信息 选择类型 选择订阅号或者服务号

【APICloud系列|33】移动应用软件加固步骤,适合所有的安卓应用市场

在腾讯开放平台下载应用加固工具legutools 下载安装打开 点击获取API密钥。链接: https://cloud.tencent.com/developer/article/1385239 一、登录腾讯云账号

怎么把图片内存变小尺寸保持不变呢。

在好多应用平台要求尺寸多少*多少&#xff0c;大小<50kb. 缩小图片的内存占用&#xff0c;但尺寸即分辨率不变&#xff0c;可以改变压缩的质量来实现。下面以PS为例&#xff1a; 1、用PS打开图片&#xff0c;然后选择文件菜单里的存储为&#xff0c;格式选择png。 2、选择…

【Java必备资料包】

适合所有大厂java开发工场师。 链接&#xff1a;https://pan.baidu.com/s/1wRxRDCGvex_G2aCZ6W57vw 提取码&#xff1a;e9xu

SaaS系列介绍之十一: SaaS商业模式分析

1 配置模式 中国企业很多是人治&#xff0c;管理弹性非常大&#xff0c;公司的政策经常变化&#xff0c;管理流程、业务变化也非常大&#xff0c;发展也非常快;一个公司今年是10个人&#xff0c;明年是100个人&#xff0c;后年可能是1000人。管理机制、方法处于经常变化的状态&…

真机x86 android分辨率,Android-x86入门之--启动参数设置

之前的文章里面我也有提到分辨率的设置&#xff0c;这里就详细说下。Android-x86启动的时候有四个选项&#xff0c;大家都知道&#xff0c;第一个是高密度&#xff0c;第二个是低密度&#xff0c;两个启动之后的效果是不一样的&#xff0c;一个图标大&#xff0c;一个小&#x…

【APICloud系列|32】iOS 上架去除Icon图像中的alpha通道或透明度

上架的时候你们有没有遇到过这个问题?? 打开图片的“显示简介”,你会发现有个叫“alpha通道”的东西,(如果是“是”你的图片就不能上架)如下图: 第一种方法 怎样解决。把你的图片用PS打开,如下图选择进行操作:

网站如何快速搭建即时响应客服系统

首先简单说明一下情况&#xff0c;公司原来的网站是用微起点快速建站做的&#xff0c;几天就搭建完成啦&#xff0c;最近又原生开发了一个大数据网站。 自己在原来的网站搭建了一个客服系统&#xff0c;后来老板不喜欢&#xff0c;我又给取消啦。 直入主题啦 登录百度账号 选…

在NetBeans,Eclipse,IntelliJ,OpenShift和Maven上使用WildFly 9

WildFly 9 CR1是最近发布的。 包括许多很酷的功能&#xff1a; 智能负载均衡 HTTP / 2和SPDY支持 新的离线CLI模式 正常关闭单节点 新的仅Servlet发行版 而且这超出了通常的Java EE 7兼容性&#xff01; 该博客是一个快速检查&#xff0c;以验证它是否可以在所有三个主要…

【CSS】font样式简写(转)- 不是很建议简写

一、字体属性主要包括下面几个 font-family&#xff0c;font-style&#xff0c;font-variant&#xff0c;font-weight&#xff0c;font-size&#xff0c;fontfont-family&#xff08;字体族&#xff09;: “Arial”、“Times New Roman”、“宋体”、“黑体”等;font-style&…

Bluetooth LE(低功耗蓝牙) - 第一部分

前言 在写这篇文章的时候&#xff0c;谷歌刚刚发布了Android Wear &#xff0c;摩托罗拉也发布了 Moto 360 智能手表。Android Wear的API还是相当基本的&#xff0c;是很好的文档材料&#xff0c;而且还会不断的更新&#xff0c;所以我不打算写一个关于他们的教程&#xff08;至…

Android查看web日志,详解Android WebView监听console错误信息

根据需求&#xff0c;我们要拿到h5的错误信息&#xff0c;并将error信息进行上报。查询了下Android WebView的API发现了WebChromeClient这个方法可以满足要求&#xff1a;Overridepublic boolean onConsoleMessage(ConsoleMessage consoleMessage) {//获取log的级别switch (con…

Hibernate READ_ONLY CacheConcurrencyStrategy如何工作

介绍 正如我前面所解释的 &#xff0c;企业的高速缓存需要勤奋。 由于数据在数据库&#xff08; 记录系统 &#xff09;和缓存层之间重复&#xff0c;因此我们需要确保两个单独的数据源不会分开。 如果缓存的数据是不可变的&#xff08;数据库和缓存都无法修改它&#xff09;&…

漫谈:Java和Python现在都挺火,我应该怎么选?Java和Python优缺点比较。

http://www.bossqiang.com/article/4 声明&#xff1a;这是一篇容易引起撕逼的文章&#xff0c;为了祖国和谐&#xff0c;人民安康&#xff0c;请各位看官尽量理性讨论。同时&#xff0c;这篇文章是面向一些初入行的朋友进行一些相对中肯一点的分析和建议而已&#xff0c;虽然…

Spring集成和Web服务

本文是我们名为“ Spring Integration for EAI ”的学院课程的一部分。 在本课程中&#xff0c;向您介绍了企业应用程序集成模式以及Spring Integration如何解决它们。 接下来&#xff0c;您将深入研究Spring Integration的基础知识&#xff0c;例如通道&#xff0c;转换器和适…