长远领会abstract class和interface


abstract
class和interface是Java语言中对于抽象类定义进行支撑的二种机制,正是出于这三种体制的存在,才给予了Java强大的面向对象能力。abstract
class和interface之间在对于抽象类定义的帮助方面享有很大的相似性,甚至足以并行替换,由此不少开发者在开展抽象类定义时对于abstract
class和interface的取舍显得比较自由。其实,两者之间仍旧有很大的分其余,对于它们的挑三拣四如故反映出对于问题领域本质的知情、对于规划意图的接头是还是不是正确、合理。本文将对它们之间的分裂进行一番剖析,试图给开发者提供一个在二者之间举办分选的基于。


掌握抽象类

abstract
class和interface在Java语言中都是用来进展抽象类(本文中的抽象类并非从abstract
class翻译而来,它意味着的是一个抽象体,而abstract
class为Java语言中用于定义抽象类的一种办法,请读者注意区分)定义的,那么哪些是抽象类,使用抽象类能为我们带来什么样便宜呢?

在面向对象的概念中,我们清楚所有的目的都是透过类来描写的,不过转头却不是那般。并不是独具的类都是用来描写对象的,借使一个类中没有包罗丰硕的信息来描写一个切实的靶子,那样的类就是抽象类。抽象类往往用来表征大家在对题目领域进行分析、设计中汲取的抽象概念,是对一多元看上去不一致,不过精神上平等的现实性概念的纸上谈兵。比如:倘使我们开展一个图纸编辑软件的付出,就会意识问题领域存在着圆、三角形那样有些有血有肉概念,它们是见仁见智的,可是它们又都属于形状那样一个概念,形状这几个定义在题材领域是不存在的,它就是一个抽象概念。正是因为虚无的定义在问题领域尚未对号入座的有血有肉概念,所以用以表征抽象概念的抽象类是不可见实例化的。

在面向对象领域,抽象类紧要用来举办项目隐藏。大家可以协会出一个稳住的一组行为的抽象描述,不过那组行为却可以有自由个可能的现实贯彻情势。那个抽象描述就是抽象类,而这一组自由个可能的求实已毕则呈现为有着可能的派生类。模块能够操作一个抽象体。由于模块依赖于一个原则性的抽象体,由此它能够是不允许修改的;同时,通过从那些抽象体派生,也可增加此模块的一言一动功用。熟稔OCP的读者必定驾驭,为了可以完结面向对象设计的一个最主题的规则OCP(
Open-Closed Principle),抽象类是里面的关键所在。


从语法定义层面看abstract class和interface

在语法层面,Java语言对于abstract
class和interface给出了分裂的概念格局,上面以定义一个名为Demo的抽象类为例来表明那种分裂。

应用abstract class的方式定义Demo抽象类的方式如下:

abstract class Demo {
    abstract void method1();
    abstract void method2();
    …
}

采纳interface的办法定义Demo抽象类的办法如下:

interface Demo {
    void method1();
    void method2();
    …
}

在abstract
class方式中,Demo可以有协调的数量成员,也得以有非abstarct的分子方法,而在interface情势的落实中,Demo只可以够有静态的不可能被修改的数额成员(也就是必须是static
final的,不过在interface中一般不定义数据成员),所有的积极分子方法都是abstract的。从某种意义上说,interface是一种特有方式的abstract
class。

对此abstract
class和interface在语法定义层面愈来愈多的细节问题,不是本文的最主要,不再赘述,读者可以参照参考文献〔1〕获得更多的相关内容。


从编程层面看abstract class和interface

从编程的角度来看,abstract class和interface都得以用来兑现”design by
contract”的思想。不过在实际的行使方面如故有一部分区分的。

第一,abstract
class在Java语言中象征的是一种持续关系,一个类只好动用三回连续关系。可是,一个类却得以兑现多个interface。也许,那是Java语言的设计者在设想Java对于多重继承的支撑地点的一种折中考虑吧。

其次,在abstract
class的概念中,我们得以给予方法的默许行为。不过在interface的定义中,方法却无法抱有默许行为,为了绕过这些限制,必须使用委托,可是那会
增添一些繁杂,有时会招致很大的坚苦。

在抽象类中不可能定义默许行为还留存另一个相比较严重的题目,这就是可能会造成维护上的麻烦。因为只要后来想修改类的界面(一般通过abstract
class或者interface来代表)以适应新的情事(比如,添加新的法门或者给已用的法门中添加新的参数)时,就会要命的麻烦,可能要费用很多的流年(对于派生类很多的意况,尤为如此)。可是若是界面是透过abstract
class来落到实处的,那么可能就只需要修改定义在abstract
class中的默许行为就可以了。
一致,假若不可以在抽象类中定义默许行为,就会促成同样的措施完结出现在该抽象类的每一个派生类中,违反了”one
rule,one
place”原则,造成代码重复,同样不便宜将来的维护。因而,在abstract
class和interface间举办抉择时要非凡的小心。


从设计理念层面看abstract class和interface

地点根本从语法定义和编程的角度阐释了abstract
class和interface的界别,那些层面的界别是比较低层次的、非本质的。本小节将从另一个局面:abstract
class和interface所反映出的统筹理念,来分析一下互相的分别。小编认为,从这些局面开展解析才能清楚两者概念的真相所在。

面前早已提到过,abstarct
class在Java语言中浮现了一种持续关系,要想使得后续关系创建,父类和派生类之间必须存在”is
a”关系,即父类和派生类在概念本质上应该是平等的(参考文献〔3〕中有至于”is
a”关系的大篇幅深入的演讲,有趣味的读者可以参照)。对于interface
来说则不然,并不需求interface的完成者和interface定义在概念本质上是相同的,仅仅是完结了interface定义的契约而已。为了使论述便于领会,下边将透过一个简短的实例进行求证。

考虑那样一个例证,即使在大家的问题领域中有一个有关Door的抽象概念,该Door具有执行三个动作open和close,此时我们得以由此abstract
class或者interface来定义一个象征该抽象概念的品种,定义情势分别如下所示:

选取abstract class形式定义Door:

abstract class Door {
    abstract void open();
    abstract void close();
}

运用interface形式定义Door:

interface Door {
    void open();
    void close();
}

任何实际的Door类型可以extends使用abstract
class格局定义的Door或者implements使用interface形式定义的Door。看起来好像使用abstract
class和interface没有大的分歧。

假定现在须求Door还要具备报警的机能。大家该怎么统筹针对该例子的类社团吧(在本例中,紧若是为了浮现abstract
class和interface反映在安顿意见上的分别,其余地点毫无干系的题目都做了简化或者忽视)?上边将罗列出可能的缓解方案,并从设计理念层面对这个差距的方案进行剖析。

解决方案一:

一句话来说的在Door的概念中扩展一个alarm方法,如下:

abstract class Door {
    abstract void open();
    abstract void close();
    abstract void alarm();
}

或者

interface Door {
    void open();
    void close();
    void alarm();
}

那就是说富有报警作用的AlarmDoor的定义格局如下:

class AlarmDoor extends Door {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

或者

class AlarmDoor implements Door {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

那种措施违反了面向对象设计中的一个基本标准ISP(Interface Segregation
Priciple),在Door的概念中把Door概念本身固有的作为格局和此外一个定义”报警器”的行事艺术混在了一道。那样引起的一个题材是那几个单纯重视于Door这么些定义的模块会因为”报警器”这几个概念的变更(比如:修改alarm方法的参数)而改变,反之依旧。

不留余地方案二:

既然open、close和alarm属于多个不等的概念,根据ISP原则应该把它们分别定义在表示那两个概念的抽象类中。定义格局有:这三个概念都施用abstract
class格局定义;五个概念都选拔interface形式定义;一个概念使用abstract
class格局定义,另一个定义使用interface格局定义。

鲜明,由于Java语言不协助多重继承,所以七个概念都使用abstract
class方式定义是不可行的。后边二种办法都是卓有功用的,可是对于它们的取舍却呈现出对于问题领域中的概念本质的知道、对于规划意图的浮现是或不是正确、合理。大家逐条来分析、表明。

即使四个概念都选用interface形式来定义,那么就反映出多少个问题:
1、我们兴许没有精晓驾驭问题领域,AlarmDoor在概念本质上究竟是Door照旧报警器?
2、若是大家对此问题领域的知情不成问题,比如:大家通过对于问题领域的辨析发现AlarmDoor在概念本质上和Door是如出一辙的,那么大家在促成时就向来不可能科学的颁布大家的安插性意图,因为在这多少个概念的概念上(均采取interface情势定义)反映不出上述意义。

若是大家对此问题领域的敞亮是:AlarmDoor在概念本质上是Door,同时它有颇具报警的机能。我们该怎么着来安排、已毕来明确的突显出大家的情趣吧?前边早已说过,abstract
class在Java语言中意味一种持续关系,而延续关系在精神上是”is
a”关系。所以对于Door这些定义,大家相应利用abstarct
class格局来定义。此外,AlarmDoor又拥有报警功用,表达它又能够成功报警概念中定义的作为,所以报警概念可以通过interface情势定义。如下所示:

abstract class Door {
    abstract void open();
    abstract void close();
}
interface Alarm {
    void alarm();
}
class AlarmDoor extends Door implements Alarm {
    void open() { … }
    void close() { … }
    void alarm() { … }
}

那种已毕格局大多可以肯定的彰显出大家对此问题领域的了然,正确的揭橥大家的筹划意图。其实abstract
class表示的是”is a”关系,interface表示的是”like
a”关系,我们在选用时方可看作一个依照,当然那是确立在对问题领域的敞亮上的,比如:即使大家以为AlarmDoor在概念本质上是报警器,同时又兼备Door的功力,那么上述的定义格局就要扭转了。


结论

abstract
class和interface是Java语言中的二种概念抽象类的点子,它们中间有很大的相似性。可是对于它们的选料却又频仍反映出对于问题领域中的概念本质的通晓、对于规划意图的突显是还是不是科学、合理,因为它们表现了定义间的例外的涉及(纵然都能够达成须求的功能)。那事实上也是言语的一种的惯用法,希望读者对象可以细细体会。


参考资料

[1] Thinking in Java, Bruce Eckel
[2] Design Patterns Explained: A New Perspective on Object-Oriented
Design, Alan Shalloway and James R. Trott
[3] Effective C++: 50 Specific Ways to Improve Your Programs and
Design, Scott Meyers


相关文章