专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

抽象类的定义,抽象类和接口的区别

来源:小易整编  作者:小易  发布时间:2023-03-21 02:44
摘要:抽象类的定义,抽象类和接口的区别在面向对象编程中,抽象类和接口是两个经常被用到的语法概念,是面向对象四大特性,以及很多设计模式、设计思想、设计原则编程实现的基础。比如,我们可以使用接口来实现面向对象的抽象特性、多态特性和基于接口而非实现的设...

// 父类:非抽象类,就是普通的类. 删除了log(),doLog(),新增了isLoggable(). public class Logger {  private String name;  private boolean enabled;  private Level minPermittedLevel;  public Logger(String name, boolean enabled, Level minPermittedLevel) {    //...构造函数不变,代码省略...  }  protected boolean isLoggable() {    boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());    return loggable;  } } // 子类:输出日志到文件 public class FileLogger extends Logger {  private Writer fileWriter;  public FileLogger(String name, boolean enabled,    Level minPermittedLevel, String filepath) {    //...构造函数不变,代码省略...  }    public void log(Level level, String mesage) {    if (!isLoggable()) return;    // 格式化level和message,输出到日志文件    fileWriter.write(...);  } } // 子类: 输出日志到消息中间件(比如kafka) public class MessageQueueLogger extends Logger {  private MessageQueueClient msgQueueClient;    public MessageQueueLogger(String name, boolean enabled,    Level minPermittedLevel, MessageQueueClient msgQueueClient) {    //...构造函数不变,代码省略...  }    public void log(Level level, String mesage) {    if (!isLoggable()) return;    // 格式化level和message,输出到消息中间件    msgQueueClient.send(...);  } }

这个设计思路虽然达到了代码复用的目的,但是无法使用多态特性了。像下面这样编写代码,就会出现编译错误,因为Logger中并没有定义log()方法。

Logger logger = new FileLogger("access-log", true, Level.WARN, "/users/wangzheng/access.log"); logger.log(Level.ERROR, "This is a test log message.");

你可能会说,这个问题解决起来很简单啊。我们在Logger父类中,定义一个空的log()方法,让子类重写父类的log()方法,实现自己的记录日志的逻辑,不就可以了吗?

public class Logger {  // ...省略部分代码...  public void log(Level level, String mesage) { // do nothing... } } public class FileLogger extends Logger {  // ...省略部分代码...  @Override  public void log(Level level, String mesage) {    if (!isLoggable()) return;    // 格式化level和message,输出到日志文件    fileWriter.write(...);  } } public class MessageQueueLogger extends Logger {  // ...省略部分代码...  @Override  public void log(Level level, String mesage) {    if (!isLoggable()) return;    // 格式化level和message,输出到消息中间件    msgQueueClient.send(...);  } }

这个设计思路能用,但是,它显然没有之前通过抽象类的实现思路优雅。我为什么这么说呢?主要有以下几点原因。

  • 在Logger中定义一个空的方法,会影响代码的可读性。如果我们不熟悉Logger背后的设计思想,代码注释又不怎么给力,我们在阅读Logger代码的时候,就可能对为什么定义一个空的log()方法而感到疑惑,需要查看Logger、FileLogger、MessageQueueLogger之间的继承关系,才能弄明白其设计意图。

  • 当创建一个新的子类继承Logger父类的时候,我们有可能会忘记重新实现log()方法。之前基于抽象类的设计思路,编译器会强制要求子类重写log()方法,否则会报编译错误。你可能会说,我既然要定义一个新的Logger子类,怎么会忘记重新实现log()方法呢?我们举的例子比较简单,Logger中的方法不多,代码行数也很少。但是,如果Logger有几百行,有n多方法,除非你对Logger的设计非常熟悉,否则忘记重新实现log()方法,也不是不可能的。

  • Logger可以被实例化,换句话说,我们可以new一个Logger出来,并且调用空的log()方法。这也增加了类被误用的风险。当然,这个问题可以通过设置私有的构造函数的方式来解决。不过,显然没有通过抽象类来的优雅。

其次,我们再来看一下,我们为什么需要接口?它能够解决什么编程问题?

抽象类更多的是为了代码复用,而接口就更侧重于解耦。接口是对行为的一种抽象,相当于一组协议或者契约,你可以联想类比一下API接口。调用者只需要关注抽象的接口,不需要了解具体的实现,具体的实现代码对调用者透明。接口实现了约定和实现相分离,可以降低代码间的耦合性,提高代码的可扩展性。

实际上,接口是一个比抽象类应用更加广泛、更加重要的知识点。比如,我们经常提到的“基于接口而非实现编程”,就是一条几乎天天会用到,并且能极大地提高代码的灵活性、扩展性的设计思想。关于接口这个知识点,我会单独再用一节课的时间,更加详细全面的讲解,这里就不展开了。

如何模拟抽象类和接口两个语法概念?

在前面举的例子中,我们使用Java的接口语法实现了一个Filter过滤器。不过,如果你熟悉的是C++这种编程语言,你可能会说,C++只有抽象类,并没有接口,那从代码实现的角度上来说,是不是就无法实现Filter的设计思路了呢?

实际上,我们可以通过抽象类来模拟接口。怎么来模拟呢?这是一个不错的面试题,你可以先思考一下,然后再来看我的讲解。

我们先来回忆一下接口的定义:接口中没有成员变量,只有方法声明,没有方法实现,实现接口的类必须实现接口中的所有方法。只要满足这样几点,从设计的角度上来说,我们就可以把它叫作接口。实际上,要满足接口的这些语法特性并不难。在下面这段C++代码中,我们就用抽象类模拟了一个接口(下面这段代码实际上是策略模式中的一段代码)。

class Strategy { // 用抽象类模拟接口  public:    ~Strategy();    virtual void algorithm()=0;  protected:    Strategy(); };

抽象类Strategy没有定义任何属性,并且所有的方法都声明为virtual类型(等同于Java中的abstract关键字),这样,所有的方法都不能有代码实现,并且所有继承这个抽象类的子类,都要实现这些方法。从语法特性上来看,这个抽象类就相当于一个接口。

不过,如果你熟悉的既不是Java,也不是C++,而是现在比较流行的动态编程语言,比如Python、Ruby等,你可能还会有疑问:在这些动态语言中,不仅没有接口的概念,也没有类似abstract、virtual这样的关键字来定义抽象类,那该如何实现上面的讲到的Filter、Logger的设计思路呢?实际上,除了用抽象类来模拟接口之外,我们还可以用普通类来模拟接口。具体的Java代码实现如下所示。

public class MockInteface {  protected MockInteface() {}  public void funcA() {    throw new MethodUnSupportedException();  } }

我们知道类中的方法必须包含实现,这个不符合接口的定义。但是,我们可以让类中的方法抛出MethodUnSupportedException异常,来模拟不包含实现的接口,并且能强迫子类在继承这个父类的时候,都去主动实现父类的方法,否则就会在运行时抛出异常。我们将构造函数设置成protected属性的,这样就能避免非同包下的类去实例化MockInterface。不过,这样还是无法避免同包中的类去实例化MockInterface。为了解决这个问题,我们可以学习Google Guava中@VisibleForTesting注解的做法,自定义一个注解,人为表明不可实例化。

刚刚我们讲了如何用抽象类来模拟接口,以及如何用普通类来模拟接口,那如何用普通类来模拟抽象类呢?这个问题留给你自己思考,你可以留言说说你的实现方法。

实际上,对于动态编程语言来说,还有一种对接口支持的策略,那就是duck-typing。我们在上一节课中讲到多态的时候也有讲过,你可以再回忆一下。

如何决定该用抽象类还是接口?

刚刚的讲解可能有些偏理论,现在,我们就从真实项目开发的角度来看一下,在代码设计、编程开发的时候,什么时候该用抽象类?什么时候该用接口?

实际上,判断的标准很简单。如果我们要表示一种is-a的关系,并且是为了解决代码复用的问题,我们就用抽象类;如果我们要表示_x0008_一种has-a关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。

从类的继承层次上来看,抽象类是一种自下而上的设计思路,先有子类的代码重复,然后再抽象成上层的父类(也就是抽象类)。而接口正好相反,它是一种自上而下的设计思路。我们在编程的时候,一般都是先设计接口,再去考虑具体的实现。

重点回顾

1.抽象类和接口的语法特性

抽象类不允许被实例化,只能被继承。它可以包含属性和方法。方法既可以包含代码实现,也可以不包含代码实现。不包含代码实现的方法叫作抽象方法。子类继承抽象类,必须实现抽象类中的所有抽象方法。接口不能包含属性,只能声明方法,方法不能包含代码实现。类实现接口的时候,必须实现接口中声明的所有方法。

2.抽象类和接口存在的意义

抽象类是对成员变量和方法的抽象,是一种is-a关系,是为了解决代码复用问题。接口仅仅是对方法的抽象,是一种has-a关系,表示具有某一组行为特性,是为了解决解耦问题,隔离接口和具体的实现,提高代码的扩展性。

3.抽象类和接口的应用场景区别

什么时候该用抽象类?什么时候该用接口?实际上,判断的标准很简单。如果要表示一种is-a的关系,并且是为了解决代码复用问题,我们就用抽象类;如果要表示_x0008_一种has-a关系,并且是为了解决抽象而非代码复用问题,那我们就用接口。


本文地址:百科问答频道 https://www.neebe.cn/wenda/934773_2.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!

共2页 1 2 当前是最后一页

百科问答
小编:小易整编
相关文章相关阅读
  • 键盘接口是什么意思?

    键盘接口是什么意思?

    键盘接口一般是用于挂接键盘的接口。它也称为键盘插座或键盘接头,它是一个连接键盘和计算机系统之间的重要硬件部件。键盘接口可以用来接收键盘上发出信号,以便在主板上发出完整的指令。键盘接口可以分为有线和无线两种,有线键盘接口是指采用插孔的连接方...

  • 机箱风扇接口接的位置介绍

    机箱风扇接口接的位置介绍

    电脑使用长时间主机会堆积很多灰尘,特别是主机风扇转动,灰尘是很多的,有的会p电脑的网友就会自己购买新风扇来更换安装新风扇,可是问题就出来了,找不到风扇的接口,那么机箱风扇接口接哪里呢?说的这里其实对于不熟悉电路的网友可能还不清楚机箱的内部结...

  • 键盘接口损坏怎么修复?

    键盘接口损坏怎么修复?

    按道理说,主板上的键盘接口接上键盘后,又不经常拔插,不应该容易损坏的。但在实际工作中键盘接口损坏的主板却又非常多,大多表现为起初是偶尔启动计算机时主机报键盘错误,按“F1”继续能够正常操作,再后来就是键盘有时能够使用有时不能够使用,到最后键...

  • 离线导航与在线导航有什么区别(开车用离线导航还是离线地图)

    离线导航与在线导航有什么区别(开车用离线导航还是离线地图)

    最早之前,开车要是不认识路,就需要下车找人询问,复杂的道路往往问了一次又一次才能找到。现在不认识路已经不是问题了,汽车上的导航都很准确,你完全没去过的地方跟着导航就能轻松找到。不过不知道大家注意到没有,现在的车上的导航有离线模式和在线模式,...

  • SFP接口是什么意思?

    SFP接口是什么意思?

    SFP接口(SmallFormFactorPluggable)是指网络标准尺寸(MSA)提出的一种高速光纤传输产品,主要用于提供高端网络应用系统,如以太网,快速以太网,千兆以太网,FDDI和ATM中继网络。它是网络应用中最常见的、最...

  • TPM接口是什么意思?

    TPM接口是什么意思?

    TPM(TrustedPlatformModule),又叫被信任平台模块,是一种模块化解决方案,它可以将与计算机系统安全相关的特定功能集成到计算机硬件中,以确保计算机系统的安全。TPM提供了在软件中复制不可能的安全服务,它可以使计算...

  • 个人支付宝api接口步骤

    个人支付宝api接口步骤

    个人支付宝api接口我们应该怎么弄呢,今天我们就来说说。操作方法01首先我们要找到的一定是支付宝官网,不然你的隐秘信息可能会泄露。然后登录或者注册支付宝账号。02...

  • 网线接口是什么意思?

    网线接口是什么意思?

    网线接口(也称为RJ-45接口)是一种用于连接电子设备/计算机之间的网络连接一体型接口,它由8根双绞合线组成,它们分别编号(1-8)。最常见的使用场景,就是将网线接口用于连接局域网中相邻电脑设备(如路由器、交换机、防火墙等)、局域网和In...

  • 周排行
  • 月排行
  • 年排行

精彩推荐