`
ericbaner
  • 浏览: 173406 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

接口与抽象类

 
阅读更多
以下为选摘自网络,罗列至此:
接口就跟U盘的USB接口一样,可以插拔。。
当你要完成一个功能的时候,两个事物联系不大,可以用接口。
抽象类着重继承关系。如果两个东西可以看成继承关系,用抽象类。
你要弄一个有警报器的门。
你可以定义一个抽象door类,门有close和open的方法。也定义到这个抽象类里。
但这个警报器,跟门一般关系不大。。所以,你可以用接口.

然后这个带警报器的门就可以是继承door这个类并实现警报器接口。

 

抽象类:首先抽象类也是一个类,什么是类呢?类就是一些对象的抽象,所以抽象类也要是能抽象出来的,比如说人,人就是男人和女人的抽象,抽象类还有另一个特点,就是不能被实例化,比如说人:人分为男人和女人,就不能再分了,所以new一个人的时候,要么new一个男人,要么new一个女人,单独的new一个人就没有任何意义,因为人这个概念不具体,所以不能new
接口:接口是表示一组功能,只要实现这个接口便能拥有这个功能,比如飞,你可以把飞抽象出一个接口,你想让谁飞,那就让它实现这个接口就行了。
接口一旦被定义和接受,就必须保持不变,以保护为使用该接口而编写的应用程序。接口发布后,就不能对其进行更改。这是我们进行组件设计的一个重要原则,叫做 ‘接口不变性’
共同点是他们都可以实现多态。
不同点在于设计抽象类和接口的目的和使用方式有所不同,抽象类是为继承而精心设计的,接口则更多以组合的方式使用。

如果你的业务中可以抽象出一个通用的处理过程,只是某些局部传递的状态或参数有所不同,这时可以考虑使用抽象类。否则在一般的情况下,优先使用接口和组合的形式,这样使代码之间的耦合度降低。

一个比较典型的抽象类使用范例是模板模式,当然也有使用组合来实现的模板。另一个使用抽象类的情形,比如很多场合下特别是对一个声明了相当数量方法的接口,提供一个类似模板的默认实现类是很有好处的,比如spring提供的一些template,dom4j提供的VisitorSupport等。甚至在effective java里已经将这种方式提倡成一种最佳实践。
接口和抽象类都能描述一般性的公有特征。一般来说,强是关系(strong is-a relationship)清晰地描述了父子关系,应该用类模拟,比如苹果是一种水果;而弱是关系(weak is-a relationship)是指对象具有某种属性,适合用接口模拟,比如苹果是可以吃的。

由于子类只能扩展一个父类,而能实现多个接口,所以接口比抽象类更灵活。但是接口不能包括具体的方法,而抽象类可以,要将两种好处结合起来,可以创建一个接口和一个实现该接口的抽象类(便利类),然后根据情况决定使用哪个。

接口不变性是指不改变接口的使用方法,但是可以改变接口的具体实现方法,而接口的实现对用户是透明的,用户不需要关心接口的实现,只要知道如何使用接口就行了,所以接口不变性为用户提供了便利。
抽象类也一样,内部实现可以因为需要而更改(比如改进了算法),但是提供给用户的使用方法不要轻易改变
接口只有方法声明没实现,本质上是一个规约;抽象类可以有实现。
语法上的区别就不说了;
从编程的角度,如果你想实现某些功能代码的重用,显然要用抽象类;
从设计的层面看,抽象类和派生类是Is-a的关系(就好像狗是动物、鹅是动物);而接口和实现类就没有这个关系(就好像狗可以看门、鹅也能看门,但它们本质上不是“看门器”)
抽象类中有属性及属性值。
接口中除了常量外,只能有方法。

通常,反映一个类的基本属性的父类,用抽象类。
接口通常比较简洁,目的明确,推崇多用接口。

我们可以把父类都写成接口,到了方便的情况下将其中一个接口改成父类。
接口       更像是特定的功能,但不明确给谁用
抽象类   更像是知道儿子什么样,就是去猜他老爸的样子。
1:声明方法的存在而不去实现它的类被叫做抽象类,抽象类中有抽象方法,需要继承它的类去重写。
2:接口中的所有方法必须是抽象方法,由实现它的类重写。
3:java是单继承,多实现的机制,如果你已经继承了一个类,还需要继承其他类,不妨去实现接口来的灵活,因为类只能继承一个,接口却可以实现很多
接口是一种协定,抽象类则相当于类模板。
如果一个类型必须实现多个协定,或者协定适用于多种类型,使用接口。
虽然抽象类和接口都支持将协定与实现分离开来,但接口不能指定以后版本中的新成员,而抽象类可以根据需要添加成员以支持更多功能。
如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。

如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。

 

如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。

 

如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。

什么时候该用抽象,什么时候用接口呢?

   A. 根据我的理解,抽象强调的是 is-a 关系,即一种继承层次(比如模板方法模式),如果有多个模板且这些模板的基本结构相似,那么可以抽象出一个父模板,即使用抽象。

   B. 而接口更侧重的是职责(最小接口原则、接口分离原则等等),接口定义的是一种合约或者规范(因为不提供任何实现)。

   C. 针对接口编程比针对抽象类编程会有更小的耦合度,因为针对抽象类编程,Client还是会与抽象类耦合

   举个例子,我们已经有Cat和Dog两个类,显然它们都是哺乳动物,从这一层次来看,我们应该为它们抽象出一个Mammal(哺乳动物的英文)类。而它们都有walk和suckle(哺乳)行为(还有其他行为,简单起见,就两个行为好了)。如下图所示:

   这个很简单,每个Java初学者在学到继承章节都会遇到这个问题。而针对接口编程如何引出呢?不着急。假设现在我们需要扩展两个鸟类(Bird),比如Swallow(燕子)和Eagle(老鹰),最简单的方法当然是另外创建一个Bird父类,父类下面有两个子类:Swallow和Eagle。因为鸟类和哺乳动物都是动物,所以再抽象一个Animal父类。

   现在总共有七个类,试想如果我们又需要增加Shark(鲸鱼)和Octopus(章鱼)两类,那么我们又要增加一个Acquatic类,其下两个子类,Acquatic又继承了Animal类,现在有十个类。

   如果继续下去,类将越来越多,即产生所谓的”类爆炸“问题。

   再进一步,我们要考虑一些特殊的情况:蝙蝠、鲸鱼、飞鱼。

   首先蝙蝠既是哺乳动物,但也有鸟类的行为fly()。鲸鱼既是哺乳动物,又具有水生动物的一些行为如suckle(),比如swim()。尤其是飞鱼,它既具有水生动物的行为swim(),同时也拥有鸟类的行为fly()。那我们该怎么办?

   让蝙蝠同时继承哺乳动物类和鸟类?让鲸鱼同时继承哺乳动物类和水生动物类?

   想法是可以的,但是这样不提会带来更加复杂的类关系,而且Java本身也不支持多继承机制。那么该如何解决?

   解决方案是使用接口,接口能弥补实现这一特性。如下:

  

   这里使用了桥接模式(抽象与实现相分离)。现在你要加一个蝙蝠类、飞鱼类、鲸鱼类都随便你,甚至你要加一个鸭嘴兽(鸟+兽+鱼的行为都有)都行。

   你或许会说这下有11个类了,比前面的还多了。不着急,我们不妨计算下,前面的例子对于每增加一个分类(水生动物或哺乳动物)增加3个类。这样如果分类有N个,且每个分类下面有2个子类,那么共有3N+1个类。而这种方法的类总数为2N+7个,随着N的增大,这种方案的优势就开始明显了。

   这就是面向接口编程的威力。   

 

以一个Door的为例,该Door具有执行两个动作 Open 和Close

此时可以用接口和抽象类两种方法来定义

---------

abstract class Door{

     abstract void open();

     abstract void close();

}

---------

interface Door{

    void open();

    void close();

}

 

其他的具体的Door可以用上述接口和抽象类中的任何一个,现在若要求Door还具有报警的功能呢 如何处理

解决方案一:

---------

abstract class Door{

     abstract void open();

     abstract void close();

     abstract void alarm();

}

interface Door{

    void open();

    void close();

    void alarm();

}

那么具有报警功能的Door如下:

 

class AlarmDoor extends Door{

      void open();

      void close();

      void alarm();

}

class AlarmDoor implements Door{

      void open();

      void close();

      void alarm();

}

 

这种方法违反了ISP原则:在Door的定义中,把Door概念本身固有的行为方法和 另外一个概念“报警器”的行为方法混在了一起,若是这样,那些仅仅依赖于Door这个概念的模块,会因为“报警器”这个概念的改变而改变。

 

解决方案二:

既然open,close和alram是两个不同的概念,根据ISP原则应该分别定义在两个概念的抽象类中,定义方式有 定义2个抽象类,定义一个抽象类一个接口。

但是Java不支持多继承 所以最后方案如下:

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

class AlarmDoor extends Door{

      void open();

      void close();

}

 

 

interface Alarm {

   void alarm()

}

 

class AlarmDoor extends Door implements Alarm{

    void open(){.....}

    void close(){.....}

    void alarm(){.....}

}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics