当前位置: 首页 > news >正文

如何理解接口-Java系列

目录结构

  • 前言
  • 什么是接口?
  • 如何声明接口?
  • 接口的作用?
  • 什么时候可以考虑使用接口?
  • 什么时候可以不考虑使用接口?
  • 在线接口文档化平台
  • 总结

前言

今天有个同事问我,如何理解接口,刚好中午下班要去吃饭,边走边说,但是都没有说到重点,所以想通过这篇文章来分享一下自己对接口的理解,阅读需要2分钟。

什么是接口?

对象通过它们公开的方法来定义它们与外界的交互行为,而方法就形成了与外界交互的接口。例如电视机的开关按钮就是你与塑料外壳另一侧的电线之间的接口,你只要按下电源开关这个按钮,就可以控制电视机开机和关机,而你不需要关注电视机开机和关机的细节,接口就是让你知道它在做什么,而无需知道它们怎么做;接口更深层的理解是:使定义(规范和约束)和实现(具体的代码逻辑)分离,它是沟通(交互)的中介物(具体实现)的抽象化

如何声明接口?

public inteface TV {

    // 开机
    public void open();
    
    // 关机
    public void close();
    
    // 选择频道
    public void selectChannel(Integer channel);
    
    // 设置声音大小
    public void setVolume(Integer vlume);
    
    ...
}
复制代码

上面声明了电视机的接口(编程语言层面的接口),暴露4个方法(与外界交互的接口),通过阅读这些方法,你脑海里大致可以对这个电视机建模,并且知道这个电视机可以做些什么,这样大家就都达成了一个共识,电视机都会具有哪些功能了;而具体要怎么实现电视机的那些功能(开机、关机、选频道、设置声音大小)呢?这就交给不同的电视机厂商吧。

// 小米电视
public inteface MiTV extends TV {
    // 扩展玩游戏接口
    public void playGame(String gameId);
}

public class MiTVImpl implements MiTV {

    @Overide
    pubilc void open() {
        // TODO 小米对开机的实现
    }
    
    @Overide
    public void close() {
        // TODO 小米对关机的实现
    }
    
    @Overide
    public void selectChannel(Integer channel) {
        // TODO 小米对选择频道的实现
    }
    
    @Overide
    public void setVolume() {
        // TODO 小米对声音大小控制的实现
    }
    
    // 扩展玩游戏接口
    @Overide
    public void playGame(String gameId) {
        // TODO 对游戏的实现
    }
}
复制代码

从上面的代码中可以知道,小米电视机不仅实现了电视机的基本操作,自身还扩展了玩游戏的接口,用户只需要通过选择小米电视提供的游戏,就可以玩游戏啦。

接口的作用?

  1. 接口即是设计:在设计层面,接口可以避免我们陷入对细节的过多思考,可以让我们站在一个更高的视角对系统做更好的评估,比如系统的交互设计是否合理,功能是否缺失,是否具备可行性,是否过于复杂等等。
  2. 接口即是约定:在编码层面,接口可以明确的告诉开发人员如何使用(接口的语义,需要什么作为输入,会有什么输出),而开发人员只需要根据这些约定去实现具体的功能代码即可。
  3. 统一类的共同行为:接口用来统一类的共通行为,当不同的类需要进行信息共享时,是不需要特别去创建类间的关系。举例来说,一个人(Human)及一只鹦鹉(Parrot)都会吹口哨(whistle),然而 Human 及 Parrot 不应该为 Whistler 的子类,最好的做法是令他们为 Animal 的子类,而他们可以使用 Whistler 的接口进行沟通。
// 吹口哨
public inteface Whistler {
    /**
     * 吹口哨
     */
    public void whistle();
}

public class JuniorWhistler implements Whistler {
    public void whistle() {
        System.out.println("入门级口哨声");
    }
}

public class SeniorWhistler implements Whistler {
    public void whistle() {
        System.out.println("高级口哨声");
    }
}

// 定义动物接口
public inteface Animal {
    /**
     * 吹口哨
     *
     * @param whistle
     * @return
     */
    public void whistle(Whistler whistle);
}

public class Human implements Animal {
    
    public void whistle(Whistler whistle) {
        whistle.whistle();
    }
    ...
}

public class Parrot implements Animal {

    public void whistle(Whistler whistle) {
        whistle.whistle();
    }
    ...
}

public class Demo {
    public static void main(String[] args) {
        // 人吹口哨
        Human human = new Human();
        human.whistle(new JuniorWhistler());  // 入门级口哨声
        
        // 鹦鹉口哨
        Human human = new Human();
        human.whistle(new SeniorWhistler()); // 高级口哨声
    }
}
复制代码
  1. 使用时无需知道实现类:当接口有实现类时,在使用它的时候无需知道它的实现类是什么(感兴趣的可以了解一下多态、依赖注入)。例如,一个事物因为口哨的噪音影响到其他人,对于其他人而言,就不需要知道噪音来源是来自人还是鹦鹉,因为他们可以确定,一个会吹口哨的事物正在吹口哨。举一个更实际的例子,排序算法可能会期待对象的类型是可以被比較的,于是它只需要知道对象的类型可以被以某种方式进行排序即可,这与对象的类型无关。whistler.whistle() 将会调用对象的实现方法 whistle,而不需要知道对象是以哪个类来实现 Whistler。
public inteface Whistler {
    public void whistle();
}

public class Human implements Animal {
    
    public void whistle(Whistler whistle) {
        whistle.whistle();
    }
    ...
}
复制代码

Human 类中的 whistle() 方法的实现 whistle.whistle() 不需要知道口哨具体的实现类是哪个,而 whistle(Whistler whistle)方法 只关注入参是 Whistler 类型就行。 至于 Java 如何实现动态绑定到具体的实现类上的方法,这个之后另开一篇文章来写。

什么时候可以考虑使用接口?

这虽然很难去定义(即使很多人一直使用面向接口编程),但个人还是根据自身的开发经验浅谈一下,如果说的不对,欢迎大家指教。

  1. 当项目没有良好的开发规范、API文档还没出、项目紧,当项目组有不少新人多时,需要使用接口,而这个接口的定义应该由有经验、对业务和项目较为了解的人去定义,这样子就可以严格约定好接口的输入输出、接口命名、参数命名,而不会被乱来,而新人只需要写对应的实现就可以,这样子在重构的时候,不会导致由于各种奇奇怪怪的问题而去改接口,一旦改了接口,出问题的可能性会更大(这个坑踩过,痛过,特别是没有单元测试来做回归测试的时候)。
  2. 当需要使用到策略模式的时候,应该基于接口来实现,比如不同国家的货币换算等。
  3. 当框架功能对于系统基于接口设计的扩展非常友好时,应该使用接口,比如依赖注入,这个时候,接口可以使系统更具扩展性,更符合Open Close原则。
  4. 基于SOA理念,暴露出来的服务必须是接口——阿里的Java开发规范手册里面就有严格说到这点。

什么时候可以不考虑使用接口?

  1. 小项目,参与的人数较少时,时间紧,可以考虑不需要使用到接口,因为接口本身就不多,无需增加各种文档化的工作量。

在线接口文档化平台

推荐的一些在线API管理平台 Swagger Editor、eolinker

  • swagger 偏向开发人员,需要掌握编写swagger文档的一些语法,上手稍慢,但界面好看,功能也强大。
  • eolinker 全部都是图形化,上手快,功能够用。

总结

本文通过一些例子和个人的开发经验,从接口的定义、接口的声明、再到接口的适用场景,来与读者分享本人对接口的理解,希望可以给读者一些收获,如果发现本人有理解不对的地方,或者有需要补充的地方,欢迎评论交流。

相关文章:

  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • Linux下的文件I/O编程
  • 作为完美主义者(强迫症)如何将linux的eth1网卡修改为eth0网卡
  • WEB服务FTP概述
  • scrapy学习之路4(itemloder的使用)
  • 基于 Docker for MAC 的 Kubernetes 本地环境搭建与应用部署
  • MQL:5分钟双均线EA
  • 我的PMP备考之路
  • 苹果承认降价电池紧缺,如果你是iPhone6 Pus用户不用再等了
  • 经典排序算法及其 Java 实现
  • Linux三剑客--awk
  • 迭代器、生成器、面向过程编程
  • 16、bash编程之数组介绍、及bash内置字符串处理工具介绍
  • 高德地图开发汇总
  • 使用Context创建一个View需要注意的地方
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • Apache Pulsar 2.1 重磅发布
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • Druid 在有赞的实践
  • ES6系统学习----从Apollo Client看解构赋值
  • Git学习与使用心得(1)—— 初始化
  • java正则表式的使用
  • Mybatis初体验
  • Sass 快速入门教程
  • SQLServer之索引简介
  • 第十八天-企业应用架构模式-基本模式
  • 关于Flux,Vuex,Redux的思考
  • 老板让我十分钟上手nx-admin
  • 我有几个粽子,和一个故事
  • 学习JavaScript数据结构与算法 — 树
  • 在weex里面使用chart图表
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • (16)Reactor的测试——响应式Spring的道法术器
  • (C)一些题4
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (分布式缓存)Redis分片集群
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (区间dp) (经典例题) 石子合并
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (学习日记)2024.01.19
  • (转)mysql使用Navicat 导出和导入数据库
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (转载)PyTorch代码规范最佳实践和样式指南
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .equals()到底是什么意思?
  • .gitignore文件—git忽略文件
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .Net 8.0 新的变化
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .Net6 Api Swagger配置