为什么要使用接口编程

栏目: IT技术 · 发布时间: 4年前

内容简介:古时的风筝接口,在 Java 中是一个抽象类型。一般来说接口只做方法的定义,不做具体的实现。

为什么要使用接口编程

古时的风筝

接口是什么

接口,在 Java 中是一个抽象类型。一般来说接口只做方法的定义,不做具体的实现。

不过在 Java 8 之后,接口类中可以定义静态变量,也可以做静态方法的实现,并且可以用 default 关键字修饰普通方法,用 default 修饰后,就可以加上方法的实现了。不过这不在今天的讨论范围内。

我们要应用一个接口,通常称作实现接口,用关键字 implements 表示,实现类必须实现接口类中定义的所有方法,并用 @Override 注解表示。

我们在日常的开发中会经常接触到接口类,如果是使用 Spring 框架的话, 通常项目结构上会按照 MVC 方式分层,在 service 层,通常是一个服务接口类对应一个服务实现类。

除此之外,在各个开源框架中,比如 Spring、Dubbo、MyBatis、Netty 这些,接口也是无所不在。我们在看一些开源框架代码的时候,正满眼放光一步一步往下跟代码不亦乐乎的时候,咔嚓就进了一个接口类中,只看到方法定义,看不到方法具体实现,然后就跟不下去了,然后还得回过头去看到底是使用的哪个具体实现类。是不是经常有这种情况。这时候就会在心里默念,接口有啥好的,严重阻碍了我都学习积极性(手动狗头)。

下面是一个接口类的定义,一个对键值对格式化的接口类,方法只有一个就是 format。

public interface DataFormatter {
   /**
    * 键值对格式化
    * @param key
    * @param value
    * @return
    */
   String format(String key,String value);
}

下面是两个具体的实现类,实现了 json 和 properties 两种格式化方式。

/**
* JsonDataFormatter
* json 格式化
* @author fengzheng
* @date 2020/3/11
*/
public class JsonDataFormatter implements DataFormatter {
   @Override
   public String format(String key, String value) {
       return String.format("{\"%s\":\"%s\"}", key, value);
  }
}
/**
* PropertiesDataFormatter
* properties 格式化
* @author fengzheng
* @date 2020/3/11
*/
public class PropertiesDataFormatter implements DataFormatter {
   @Override
   public String format(String key, String value) {
       return String.format("%s=%s", key, value);
  }
}

之后我们想使用哪种格式化方式,就实例化哪个实现类,然后调用 format 方法。

public class DataFormatTest {
   public static void main(String[] args){
       String key = "name";
       String value = "古时的风筝";
       DataFormatter jsonDataFormatter = new JsonDataFormatter();
    //{"name":"古时的风筝"}
       System.out.println(jsonDataFormatter.format(key,value));
    //name=古时的风筝
       DataFormatter propertiesFormatter = new PropertiesDataFormatter();
       System.out.println(propertiesFormatter.format(key,value));
  }
}

这个定义和使用过程恐怕在座的各位都再熟悉不过了,那么到底使用接口有什么好处呢,为什么非要把方法定义和方法实现分开呢,难道不是多此一举吗。

接下来,我将列举几个使用接口的好处,可能还有更多,欢迎补充。

接口编程的好处

通俗的来说,接口最大的好处就是可以最大限度的降低「 改变 」带来的影响,封装「 变化 」的部分。

往高维度里说,那就要牵扯出 设计模式 了,一个系统架构如果设计的好,那就一定离不开各种设计模式。而好的设计模式一般都遵循如下 7 大设计原则。

  1. 单一职责原则 (Single Responsibility Principle)

  2. 开放-关闭原则 (Open-Closed Principle)

  3. 里氏替换原则 (Liskov Substitution Principle)

  4. 依赖倒转原则 (Dependence Inversion Principle)

  5. 接口隔离原则 (Interface Segregation Principle)

  6. 迪米特法则(Law Of Demeter)

  7. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle)

重点来了,在 Java 中要实现这 7 大原则,那必定离不开接口。好多的设计模式都是通过接口实现的。

接口都这么抽象了,我们就没必要说的这么玄幻了,我们就通俗点儿说吧。

1、实现了松耦合

我在文章第一部分定义了一个键值对格式化的接口,我们还用键值对格式化这个功能举例子。假设我开始并没有定义一个接口,而是定义了一个普通的类。比如下面这个,按照 json 格式返回字符串。然后,愉快的项目中使用了这个格式化方法。

public class JsonDataFormatter {
   public String format(String key, String value) {
       return String.format("{\"%s\":\"%s\"}", key, value);
  }
}

按照故事的发展,当然是后来发生了一点变故,我的某个模块它变了,它不想要 json 格式了,想要 properties 格式了,没错,就是这么善变。

这下慌了,这怎么办,直接修改 format 方法吧,肯定不行,有的模块还是要 json 格式。新添加一个类吧,实现一个 properties 格式化的方法。

public class PropertiesDataFormatter {
   public String format(String key, String value) {
       return String.format("%s=%s", key, value);
  }
}

可以是可以,但是要对这个模块中已经调用了 json 格式化方法的地方一一做修改,你愿意这么干么,当然不愿意。

那怎么办呢,没错,定义接口,然后实现两个针对 json 和 properties 的两个实现类。就是文章第一部分所举的例子那样。

有的同学看完想了想说,那不对呀,你这样整完之后,那和重新创建一个类的方式有什么区别,该修改的地方还是要修改呀?

为什么要使用接口编程

其实不然,用了接口之后,我们 new 出来的实现类会被接口类型接收,就像下面这样:

DataFormatter jsonDataFormatter = new JsonDataFormatter();
DataFormatter propertiesFormatter = new PropertiesDataFormatter();

最终接收的类型都是 DataFormatter ,而不是具体的实现类的类型,这样一来就省去了很多事儿,否则光 import 的修改就一大堆。

当然,这并不是最优解,最好的办法是结合工厂模式,让工厂类帮你返回具体的实现类,比如下面这个实例代码这样。

public static DataFormatter create(){
   return new JsonDataFormatter();
}
DataFormatter jsonDataFormatter = create();

2、增强了扩展性

其实上面的键值对格式化的例子也有涉及到扩展性的地方。再进一步,我又在系统中加了个模块,这个模块也有键值对格式化,但是要使用特殊的格式化方式,比如 “name 是 古时的风筝” 。用了接口就简单了,增加一个实现类,实现自 DataFormatter 接口。

之后不管你新加的模块用什么样的格式化方式,你只要实现接口就行了。

最常见的就是数据库操作这块,假设我之前用的是 mysql 数据库,后来呢,数据量增加了,有些部分用上了 hbase 和 mongodb。

那怎么办,改代码吗?那简直要了亲命了。

各种数据库驱动包就是利用接口做的,Java 我只管定义接口和方法声明,你们拿去用,自己实现具体的细节。下面是 JDK 中 SQL 部分,基本上都是接口定义。

为什么要使用接口编程

然后,mysql 提供了 mysql-connector-java 驱动包,实现了 mysql 相关的操作。其他的提供商实现自家数据库的驱动包,但都要实现 JDK 定义的接口方法。

下面是 JDK 中数据库连接的接口定义,定义了两个方法 getConnection(),一个带参数,一个不带参数。

package javax.sql
public interface DataSource  extends CommonDataSource, Wrapper {
 Connection getConnection() throws SQLException;
 Connection getConnection(String username, String password)
   throws SQLException;
}

mysql-connector-java 包中,我们看到了实现此接口的类  MysqlDataSource ,实现了这两个方法。

为什么要使用接口编程

同样的,其他数据库驱动也要实现这两个方法,而我们在代码中只需要引用相关的驱动包,然后调用 DataSource getConnection 方法就可以获取数据库连接,而不用在乎到底是用了 mysql 还是其他的数据库。

另外,除了数据库连接驱动外,还有各个数据库连接池框架,比如 HikariPool、Durid 也都是通过实现各个接口来完成各自的连接池管理工作的。

3、为多种设计模式提供基础

在上面也提到了很多设计模式都离不开接口。

依赖注入模式:比如 Spring 框架的核心技术依赖注入模式,其中有一种方式就是利用接口实现的,叫做接口注入。

代理模式:Spring 中的 AOP 就是用了动态代理模式,如果启用的是 JDK 的动态代理,要被代理的类就要从一个接口实现而来。与之对应的是 CGLIB 动态代理模式,这种方式不需要接口。

其他的还有像工厂模式、适配器模式等等。

4、实现可测试的代码

当我们开发完功能后,要进行测试,但是有一些环节我们发现如果不用真实参数就运行不下去,那么如果有接口的话,我们可以实现这个接口,做一些假的模拟返回值回来,从而绕过这个步骤。

再有,比如要操作的数据是生产数据,操作具有一定的危险性。那么,我们也可以实现一个接口,做一个模拟返回。

从而达到整体功能的测试。

5、规范和安全性

有些地方说接口是为了为项目做规范,比方说,架构师负责做接口,定义方法等,从而实现结构和命名上的规范。但是,规范主要还是在于开发者自身,接口做的再标准,开发者实力不行,那项目也没办法做到规范。

还有就是安全性,假设你要使用我做的 SDK,那我把接口暴露给你就好了,接口定义、方法调用方式都写在文档里,细节不用管。就好比我前面说的,跟着跟着源代码,咔嚓进到一个接口类,这样就阻碍了一部分人的跟踪脚步。但是也只是有限的安全性,除非是远程调用(RPC)。

总结

接口为更多的设计模式提供了基础。

接口封装 变化 ,以减小之后由于改变带来的成本。那么我们在项目中怎么判断哪些地方会有变化呢?确实没有统一的标准,大多数情况下要靠项目设计者的预估和经验。

一般来说,有交互的地方发生变化的概率更大一些。比如说 Java 和数据库交互,例如上面提到的各种数据库驱动包。还有发生数据转换的地方,比如从 DAO 层向 service 层、service 层向 controller 层往往返回的数据格式和内容发生改变的可能性更大。

当然,并不是给所有可能变化的地方都用上接口就是好的,项目初始阶段还是以简单为主,否则过度设计得不偿失。

还可以读:

先从简单的源码入手:MyBatis 工作原理分析

系统内存爆满,原来是线程搞的鬼

-----------------------

公众号:古时的风筝

一个斜杠程序员,一个纯粹的技术公众号,多写 Java 相关技术文章,不排除会写其他内容。

为什么要使用接口编程

【我要升华!】

为什么要使用接口编程

以上所述就是小编给大家介绍的《为什么要使用接口编程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

疯长

疯长

[美]肖恩· 阿美拉蒂 / 中信出版集团 / 2018-10 / 45

实现财务回报以及扩大影响力是企业家长期关注和讨论的问题。 为什么有些公司实现了10倍的投资回报,而其他的则勉力支撑?产品类似的公司,为什么有的家喻户晓,有的默默无闻直至退出市场…… 为了了解真相,作者阿美拉蒂在这本书中精选10组对照公司,比如,同为社交平通的Facebook(脸谱网)和Friendster(交友网),同为快餐领域先驱的麦当劳和白色城堡,再比如都在开发电动汽车市场的特斯拉......一起来看看 《疯长》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具