什么时候使用接口(抽象类)?

一、接口的做用概述

接口的做用简单能够归纳为:c++

一、避免客户端去实例化某个类程序员

二、向上转型的使用——多态算法

三、扩展:Java中还能依靠接口实现多继承数据库

二、接口的引进

先看这样一个场景:某个果园里如今有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用 get() 方法表示,代码以下:编程

苹果类app

public class Apple {
    public void get() {
        System.out.println("获得苹果");
    }
}

香蕉类编程语言

public class Banana {
    public void get() {
        System.out.println("获得香蕉");
    }
}

客户端工具

public static void one() {
        // 实例化一个apple
        Apple apple = new Apple();
        // 实例化一个banana
        Banana banana = new Banana();

        apple.get();
        banana.get();
}

苹果和香蕉各自维持一个属于本身的 get()方法,直接使用了 new 运算符进行实例化对象的操做,以后分别调用本身的 get()方法,中规中矩的实现过程,很好,咱们完成了客户的任务。这时有了新的需求——须要用采摘到的水果作果汁,使用 doJuice(对应的水果) 方法表示,水果类的代码不变,客户端新加的其余代码以下:学习

    private static void doJuice(Apple apple) {
        apple.get();
        System.out.println("作成果汁");
    }

    private static void doJuice(Banana banana) {
        banana.get();
        System.out.println("作成果汁");
    }

客户端优化

public static void oneA() {
        // 实例化一个apple
        Apple apple = new Apple();
        // 实例化一个banana
        Banana banana = new Banana();

        doJuice(apple);
        doJuice(banana);
    }

任务完成,如今果园又引进了新品种,有橘子,西瓜,荔枝,葡萄,哈密瓜,火龙果,梨……

仍是要采摘这些水果真后作果汁,若是仍是用以前的代码实现,试想一下,除了必须加的新水果类以外,在客户端里还要为每个水果类型分别添加对应的 doJuice(水果) 方法,然而水果种类那么多……少一个就不行,怎么改进呢?

为了增长程序的灵活性,引进接口。类图修改成:

作果汁的方法只须要一个便可,参数是接口类型:

    private static void doJuiceB(Fruit fruit) {
        fruit.get();
        System.out.println("作成果汁");
    }

客户端

    private static void two() {
        // 使用接口的引用指向子类的对象,向上转型过程,用到了多态
        Fruit apple = new AppleA();
        Fruit banana = new BananaA();
        Fruit orange = new OrangeA();

        doJuiceB(apple);
        doJuiceB(banana);
        doJuiceB(orange);
    }

事实上,想把各个水果都抽象化,能够选择抽象类或者接口去实现,而如今咱们要建立不带任何方法定义和成员变量的抽象化的类,首选的应该是接口。

三、为何要定义一个接口?

接口不只仅是简单的抽象,它比对类型的抽象(抽象类)更进一步,是一种更纯粹的抽象过程。由于接口没有任何实现,没有任何和接口相关联的存储(PS,Java 8 对接口已经支持能够带实现的 default 方法),所以也就没法阻止多个接口的组合(多继承)。

前面例子里,抽象的其实更是对采集水果这个动做的抽象,用接口表示最好不过。客户端里使用了多态机制,能够把任何一个水果类对象当参数传入到 doJuice 方法里,基于这个设计,使得 Java 程序员不用再为相似的场景作出多余的努力,这就是使用接口的核心缘由之一。由于这会使得程序变得很是灵活,并且经过继承还能对接口进行扩展——旧的接口去 extends 新的接口。

使用接口的另外一个缘由是和抽象类相似——避免某个类被实例化,告诉编译器和程序员,这个类不须要实例化,咱们只是为了对某些行为作出规范,你们想用就去遵照这个规则。基于此,有的编程语言(好比 iOS 开发的 Objective-C)已经不把接口叫 interface,直接叫 protocol。

统一标准的目的是让你们都知道这个是作什么,可是不用知道怎么作,故这个类型不须要客户端去实例化,这一点和抽象类是一致的,更通俗的说接口就是个招牌。

好比说看到这个图标:

一看就知道这是 7-11 便利店,这个图标就是接口,人们远远的看到了这个接口,就知道这个店是 7-11 便利店(实现接口)。固然,这个店也能够不挂 7-11 的牌子,直接卖东西(直接写实现方法),那样咱们就不能一看见店铺门,就简单粗暴的知道这个店铺是24小时便利商店……相反,须要采起以下作法:

一、沿着马路边走到每一个店铺近前去观察,哪一个是24小时营业的?(这就是反射的实现),很显然这样一家家的问实在是很是麻烦(通常状况下,反射不如直接 new 来的直接)……

二、要么咱们就记住,xxx路xxx号店铺是24小时营业的便利店,离着它 200 米的 xxxx 号也是(这就是硬编码实现),很显然这样咱们要记住的不少东西(代码量剧增),并且若是有新的便利店开张,咱们也不可能知道(不利于扩展)……

而实现了接口,就意味着:店铺门前挂了这个招牌,咱们不用进去问,甚至不用走到店铺门前,远远看一眼,看到了这个标志,就知道这个店铺是 7-11 便利店,24小时营业的

再举一个相似的例子,你们都知道吉祥馄饨。

接口也能够比喻为吉祥馄饨的连锁招牌,每一个连锁店都有同样的馄饨菜单,同样的馄饨作法,同样的总部的馄饨配料……可是每一个店铺实现的味道,每一个店铺实现过程当中的卫生状况……你们就不知道了,只能直观的,或者从网上平台去看各个店铺的口碑(具体类对接口的不一样实现)。

一样对上一个例子—7-11便利店来讲,人们不须要去具体询问店员大家的店铺是24小时营业的便利店么?人们只须要,也只能了解到 7-11 这个牌子表明的意思就足够了……映射到程序里,就是对具体的类来讲,每个方法是怎么实现的,调用者不知道,其实也不在意。调用者它只须要知道接口的知识,也最多只能知道接口的知识,所以这是一个很好的抽象过程,和把不一样层次的工做内容快速分离的过程。

四、项目里即便只有一个类使用接口,也不厌其烦的定义接口,且从业务上看,将来也不太可能有其余类用这个接口,那定义这个接口意义在哪里?

三个字:没卵用。一个好的系统是一步步的重构,优化获得的,而不是开始就设计出来的,避免早期的过分优化!

《Thinking in Java》一书说到“肯定接口是理想选择,于是应该老是选择接口而不是具体的类,这实际上是一个诱惑!由于对于建立类,几乎任什么时候候,均可以建立接口来代替!许多人都陷入了这个陷阱,在编写代码的时候,只要有那么一丝可能就去肆无忌惮的建立接口……貌似是由于须要使用不一样的具体实现,实际上已是过分优化,已经变成了一种草率的设计!任何抽象都应该以真正的需求出发。必要的时候,应该是重构接口而不是处处添加额外级别的间接性,并所以带来额外的复杂性,恰当的原则应该是:优先想到使用类,从类开始,若是接口的设计需求变得很是明确了,好的,进行重构吧!记住,接口虽然是一个很重要的工具,可是极其容易被滥用!”

五、抽象类能代替接口么?

有人说:我以为抽象类彻底能够替代接口,接口能作的抽象类均可以,并且抽象类还能包括实现。这比接口更强大……

到这里,先打住这个提问,别忘了 Java 不支持多继承!若是只是使用抽象类则必须继承 abstract class,而 Java 只容许单继承,因此仅在这一点上,接口存在就已经十分有意义了,它解决了 Java 不容许多继承的问题。

六、接口和抽象类有什么区别,选择使用接口和抽象类的依据是什么?

接口和抽象类的概念不同。

接口是对动做的抽象,抽象类是对类型的抽象。

抽象类表示这个对象是什么,接口表示这个对象能作什么……

好比,人分男人,女人这两个类,他们的抽象类是人,这种继承关系说明他们都是人,而人均可以吃东西,然而狗也能够吃东西,咱们就能够把“吃东西”这个动做定义成一个接口,而后其余须要的类去实现它,从而具有吃的行为。

因此,在高级语言上合理的继承设计就是:一个类只能继承一个类(抽象类,正如人不可能同时是生物和非生物),可是能够实现多个接口(吃饭、走路),这点是c++的缺陷。总结来讲:

第一点. 接口是抽象类的更高一级的抽象,接口没有任何相关自身的存储,接口中全部的方法默认都是抽象的,默认都是public的,而抽象类只是声明方法的存在而不去实现它的类,须要public方法,则须要程序员指定访问权限

第二点. 接口能够多继承,抽象类不行

第三点. 接口定义方法,不能实现(Java8之后,接口也能够有default实现方法了),而抽象类能够实现部分方法,也就是抽象类能够同时存在普通方法和抽象方法

第四点. 接口中基本数据类型默认为 public static, 而抽类象不是的。

 一句话区分:当你关注一个事物的本质的时候,能够抽象共同的部分,作为抽象类;当你关注一个操做的时候,用接口。

七、接口在开发过程当中能够快速分离工做内容

上层业务的开发者在写顾客购买商品须要支付这个业务逻辑的时候须要一个功能,就是链接数据库去访问顾客的商城钱包,可是他的工做专一于实现业务逻辑,不想分开精力去作底层实现,那么他只须要先定义一个接口,而后就能够继续他的业务逻辑代码了,而底层业务(算法,数据库链接,数据存储等)实现者能够根据这个接口,作他本身具体的实现,上层调用者不须要也不该该去知道底层实现,他只须要了解到接口这一级别便可。这样经过使用接口就能够快速的分离工做内容,达到团队并行工做的目的。

此外,若是规范是经过接口定义的,那么当你这个功能有多个实现时,你只要实现了这个接口,那么能够快速的替换具体实现,作到代码层面的彻底分离。

总结起来就一句话:接口或者规范能够在开发过程当中作到工做内容的分离。

团队的 A 写接口,B 写实现,C 写实现……B、C就不用写接口,B、C 看到 A 的接口就知道我要具体去实现什么功能,供上层调用者 A 使用便可,而对于 A 来讲,不给大家统一规定好了,大家怎么知道该如何去实现哪些具体内容……

好比一样是登录操做,A 不统一规定为 login(xxx); 那么颇有可能 C 写一套登陆实现叫 loginA,B 写一套登陆实现叫 denglu……具体到程序里就会让人困惑……且没法快速的替换不一样的实现过程。

更进一步,一个任务,A做为上层调用者,它须要一个算法方面的功能,可是 A 不须要去具体学习相关算法和实现,因而 A 就去写一个接口,而让 B 这个底层开发人员写实现,可是 B 偏偏今天不在公司,A 明天要出差,任务后天就交工,那 A 今天必须把使用这个接口的代码写好,A 写好接口,明天 B 把接口实现传个实例进来,任务 ok 交工。

故 interface 换个叫法就是 contract,有点合同的意思。B实现了这个接口,表明 B 承诺能作某些事情。A 须要一些能作某些事情的东西,因而 A 要求,必须实现了接口,才能被我调用。实际上也就是个“规范”。

相关文章
相关标签/搜索