设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
17602 人已学习
课程目录
已更新 21 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (4讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
不定期加餐 (2讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
设计模式之美
登录|注册

08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?

王争 2019-11-20
在面向对象编程中,抽象类和接口是两个经常被用到的语法概念,是面向对象四大特性,以及很多设计模式、设计思想、设计原则编程实现的基础。比如,我们可以使用接口来实现面向对象的抽象特性、多态特性和基于接口而非实现的设计原则,使用抽象类来实现面向对象的继承特性和模板设计模式等等。
不过,并不是所有的面向对象编程语言都支持这两个语法概念,比如,C++ 这种编程语言只支持抽象类,不支持接口;而像 Python 这样的动态编程语言,既不支持抽象类,也不支持接口。尽管有些编程语言没有提供现成的语法来支持接口和抽象类,我们仍然可以通过一些手段来模拟实现这两个语法概念。
这两个语法概念不仅在工作中经常会被用到,在面试中也经常被提及。比如,“接口和抽象类的区别是什么?什么时候用接口?什么时候用抽象类?抽象类和接口存在的意义是什么?能解决哪些编程问题?”等等。
你可以先试着回答一下,刚刚我提出的几个问题。如果你对某些问题还有些模糊不清,那也没关系,今天,我会带你把这几个问题彻底搞清楚。下面我们就一起来看!

什么是抽象类和接口?区别在哪里?

不同的编程语言对接口和抽象类的定义方式可能有些差别,但差别并不会很大。Java 这种编程语言,既支持抽象类,也支持接口,所以,为了让你对这两个语法概念有比较直观的认识,我们拿 Java 这种编程语言来举例讲解。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(141)

  • 辣么大
    如果让我聊聊接口和抽象类,我会这么聊:定义、区别(是什么),存在意义(从哪来),应用(到哪去)。
    1、定义:
    抽象类:不允许实例化,只能被继承;可包含属性和方法,包含抽象方法;子类继承抽象类必须重写抽象方法。
    接口:不允许实例化,只能被实现;不包含属性和普通方法,包含抽象方法、静态方法、default 方法;类实现接口时,必须实现抽象方法。
    2、意义:
    抽象类:解决复用问题,适用于is-a的关系。
    接口:解决抽象问题,适用于has-a的关系。
    3、应用:
    例如:
    解决复用问题:java中的子类FileInputStream和PipeInputStream等继承抽象类InputStream。重写了read(source)方法,InputStream 中还包含其他方法,FileInputStream继承抽象类复用了父类的其他方法。
    解决抽象问题:抽象类InputStream实现了Closeable接口,该接口中包含close()抽象方法。Closeable这个接口还在很多其他类中实现了,例如Channel,Socket中都有close() 关闭这个功能,但具体实现每个类又各有不同的实现,这个就是抽象。

    4、补充知识点(语法):
    Java接口中可以定义静态方法、default方法,枚举类型,接口中还可以定义接口(嵌套)。
    public interface ILog {
      enum Type {
        LOW,
        MEDIUM,
        HIGH
      }
      interface InILog{
        void initInLog();
      }
      default void init() {
        Type t = Type.LOW;
        System.out.println(t.ordinal());
      }
      static void OS() {
    System.out.println(System.getProperty("os.name", "linux"));
      }
      void log(OutputStream out);
    }
    class ConsoleLog implements ILog {
      @Override
      public void log(OutputStream out) {
        System.out.println("ConsoleLog...");
      }
    }
    2019-11-20
    11
    98
  • NoAsk
    Java使用abstract表示抽象类,interface表示接口。
    老师讲的很好,我补充一下使用;
    1.java中抽象类是类,而java不支持多继承,当想要实现多继承的时候只能使用表示has-a的接口来实现。
    2.在java8之前,定义的接口不能有具体实现,这会导致在后续维护的时候如果想要在接口中新增一个方法,必须在所有实现类中都实现一遍,并且只有几个新的实现类可能要去具体实现,其他的都只是加上默认实现,这样比较麻烦。在java8中接口可以用使用关键字default,来实现一个默认方法,这样就解决了上述的麻烦。
    2019-11-20
    37
  • 侯金彪
    接口强调具有什么能力(has-a),
    抽象类强调是什么(is-a)。
    2019-11-20
    17
  • Daiver
    go和java 都有接口设计,但go的设计是飞入侵入性(duck type),而java必须显式实现该借口,这一点go做的真的好了。
    2019-11-20
    7
    13
  • Maurice
    嗯,刚刚看了一下评论区,小弟给大家初学设计模式以及刚刚接触项目的同学一些建议,其实在学这一章更以及往后正式的20多种设计模式时候更加锻炼的是一种抽象思维,以我个人的一点经验,就像老师一开始说的,在学习的时候,不要总是类比生活中的一些阿猫啊狗的例子,没错初次是很好理解,而且你会感觉好像懂了,但是实际上你还是不懂(可能是以为我笨),建议大家没学习一种模式的时候,思考一下现在在做的项目中那些业务可以进行优化,如果可以的话,可以进行重构,这样对你的学习才有很大的帮助;举个栗子,例如策略模式,我们在代码是不是经常调用第三方接口,每个接口都有它的规范之类的,如果你按照low 的方式就是按照规范组织一些报文请求,但是这个时候,你可以用的你的抽象思维,比如,在这类业务中,无外乎就是,1、构建报文,2、请求结构,3、解析返回,那么我就可以这样设计:
        /**
         * 请求统一接口
         */
        public interface Fun<T> {
            /**
             * 发送请求报文
             */
            String constructionRequest();
            /**
             * 请求报文
             */
            String doPost(String request);
            /**
             * 解析回执报文
             */
            T resolveResponse(String responseText);
        }
    然后再写一个策略的算法簇(应该是这么叫)
    /**
     * 统一实现接口
     */
    public class DealWithHandle {
        
        /**
         * 描述 create.
         *
         * @param <T> the type parameter
         * @param fun the fun
         * @return the t
         * @author Maurice Mao
         * @created 2018 -07-13 10:58:32 Handle t.
         */
        public static <T> T handle(Fun<T> fun) {
            
            String request = fun.constructionRequest();
            
            String response = fun.doPost(request);
            
            T t = fun.resolveResponse(response);
            
            return t;

        }
    }
    这个时候,假设你做支付宝扫码支付的时候(或者微信以及第三方支付),是不是可以这么调用:
    public class OfficialAliScan {

        private static Logger logger = LoggerFactory.getLogger(OfficialAliScan.class);

        public static ApiTradeResultDto operate(final TradeOrderDO orderDO, final SubMerchatAuthDO subMerchatAuthDO, final String authCode) {
            return DealwithHandle.handle(new DealwithHandle.Fun<ApiTradeResultDto>() {
                @Override
                public String constructionRequest() {
                      ///todo
                }

                @Override
                public String doPost(String bizContent) {
                    ///todo
                }

                @Override
                public ApiTradeResultDto resolveResponse(String responseTxt) {
                    ///todo
                }
            });
        }
    这样的话,你的业务代码组织的就很清晰,也便于调试维护
    2019-11-21
    11
  • helloworld
    『那又如何避免这个类被实例化呢?实际上很简单,我们只需要将这个类的构造函数声明为 protected 访问权限就可以了。』当把一个类的构造方法声明为protected后,在同一个包的其他类中还是可以new这个类的对象的。老师,是我想错了吗?请老师指正

    作者回复: 好像我写的是有点问题,稍后更正下,多谢指出

    2019-11-21
    6
  • 梦倚栏杆
    关于抽象类和接口有一个疑问,也可能是对业务模型不够了解,同一件事其实可以表达成is a也可以表达成has a,这个就看你的语言描述到底是个名词还是行为。
    举例说明:
    宠物猫和宠物狗都是宠物。
    宠物猫和宠物狗都有会被宠。
    is a基类定义:宠物类
    has a 接口定义:可被宠的
    如果取决于需求的语言阐述,感觉就太依赖个人表达习惯了,这不是一个团队持续迭代好项目的方式吧
    希望老师可以帮忙解惑
    2019-11-20
    1
    6
  • 大牛凯
    Python有抽象类吧?abc不是可以定义抽象类么
    2019-11-20
    2
    6
  • 五岳寻仙

    Python中抽象类是通过 abc 模块中的 abstractmethod 和 ABCMeta

    from abc import abstractmethod, ABCMeta

    class BaseClass(metaclass=ABCMeta):
        @abstractmethod
        def fun(self, x): pass

    因为 Python 需要天然支持多继承,所以可以直接用上述的抽象类实现接口
    2019-11-21
    5
  • 熊斌
    举个例子吧,附件上传服务端实现

    需求:支持上传、下载、删除以及文件的存储,存储的话需要支持存本地和云端,涉及附件上传的业务点有十几个。

    设计思路:
    1、接口设计:上传、下载、删除是公共行为,抽象到接口中

    2、存储方法是一大块公共代码,写到抽象类里面

    3、每个业务的附件上传子类实现接口、继承抽象类
    2019-11-20
    2
    5
  • Smallfly
    抽象类 vs 接口

    抽象类 接口
    实例化 否 否
    属性 是 否
    方法 是 是
    实现 是 否
    目的 复用 扩展
    意义 is-a has-a
    2019-11-20
    1
    4
  • Uncle.席
    接口强调某一方面的统一,抽象强调共性的复用
    2019-11-20
    4
  • 👽
    个人认为,jdk8中,接口的默认实现之后。接口和抽象的区别渐渐模糊了。接口,从一定程度上也可以当抽象类使用。而且最主要的是,支持多实现,但不支持多继承。
    并且,我认为,多实现也是接口和抽象类最主要的区别,没有之一!

    我的理解:
    抽象类:描述整个类的一个概况。(举例来说,抽象类,爬行动物)
    接口: 描述的是需要具备的能力。(举例来说,飞行,游泳)

    乌龟,在类上,应该继承爬行动物。因为属于爬行动物(说实话不确定是不是爬行动物),但是乌龟同时也会游泳。会游泳,就只能是乌龟实现的接口。如果,有一天,乌龟长翅膀会飞了(致敬超级马里奥),那这时候,实现会飞行接口就可以了。

    在使用到这个接口的角度,某个游戏,需要十个飞行的小怪。以前是十个各种鸟类。但是现在需要十一个了,于是乎给乌龟安上了翅膀(实现飞行接口),拿来充当飞行小怪使用。
    2019-11-25
    3
  • Java语言接口和抽象的用法老师已经在文中讲了,但在后来的语法规则里,接口也可以由default修饰符来构造默认的接口方法逻辑了,这样在实现接口的时候可以不用必须实现包含被标记为default的方法了,这样的设计是否也是因为接口中如果包含了通用的方法逻辑,可以不用每个接口都实现一遍,避免方法逻辑冗余呢?也是复用的一种思想?
    2019-11-20
    3
  • 香蕉派2号
    问题1:
    c#接口两种实现方式:
    // 直接实现接口
    interface IAttack
    {
        void Attck();
    }
    class Gun : IAttack
    {
        public void Attck()
        {
            throw new NotImplementedException();
        }
    }
    class Rifle : IAttack
    {
        public void Attck()
        {
            throw new NotImplementedException();
        }
    }

    // 显示实现接口(当多个接口的中需要实现的方法名称一样的时候)
    interface IAttack
    {
        void Attack();
    }
    interface ISpecialAttack
    {
        void Attack();
    }
    class Gun : IAttack, ISpecialAttack
    {
        public void Attack()
        {
            Console.WriteLine("普通攻击");
            throw new NotImplementedException();
        }
        void ISpecialAttack.Attack() // 接口的显示实现
        {
            Console.WriteLine("特殊攻击");
            throw new NotImplementedException();
        }
    }
    class Rifle : IAttack, ISpecialAttack
    {
        public void Attack()
        {
            Console.WriteLine("普通攻击");
            throw new NotImplementedException();
        }
        void ISpecialAttack.Attack() // 接口的显示实现
        {
            Console.WriteLine("特殊攻击");
            throw new NotImplementedException();
        }
    }
    public class TestWeapons
    {
        public void TestMethod()
        {
            ISpecialAttack desertEagle = new Gun();
            desertEagle.Attack();

            IAttack rifle = new Rifle();
            rifle.Attack();
        }
    }


    c#抽象类定义以及实现:
    abstract class Weapon{
        public float attackRange;
        public float damage;
        public virtual void Attack(Enemy enmey)
        {
            enmey.health -= damage;
            Console.WriteLine("attack...");
        }
    }

    class Gun: Weapon
    {
        public override void Attack(Enemy enmey)
        {
            base.Attack(enmey);
        }
    }

    public class TestWeapon02
    {
        public static void main02(string[] args)
        {
            Enemy enemy = new Enemy();
            Weapon gun = new Gun()
            {
                damage = 5,
                attackRange = 100,
            };
            gun.Attack(enemy);
            Console.WriteLine("enemy health: " + enemy.health);
        }
    }

    问题2:
    总的来说,抽象类就是类别上的总称;接口就是行为上的规范

    2019-11-20
    1
    3
  • 未来、尽在我手
    在学Java时,我一直在疑惑什么时候用抽象类,什么时候用接口。现在学完对此有一点理解,也明白他们之间的关系及用法。下来需要多实践与学习。
    2019-11-20
    3
  • bigtom
    is-a 和 has-a 的地方我觉得有点问题。应该说 is-a 是继承(Inheritance),has-a 是组合(Composition)。所以我感觉无论接口还是抽象类都是继承,所以都是 is-a 的关系呀。
    2019-11-23
    2
  • 王加武
    接口和抽象类的区别
    1,接口中的所有的方法必须是抽象方法,而抽象类中的方法可以是普通方法
    2,抽象类使用abstract来修饰,接口使用的是interface来修饰
    3,在实现接口的所有的类中,接口的方法都必须被实现,而抽象类的话只有子类继承了抽象的父类,父类没有被实现的抽象方法才需要被实现
    4,接口可以实现多继承,接口是一种规则,一种契约
    5,抽象类是不可以被new的,即它的构造方法不能new
    6,抽象类更多的是用于继承的场景的,而接口则是更多的以组合的方式来使用

    学习了王老师的这节课,我对接口和抽象类的认识又加深了
    什么时候使用抽象类——
    当所涉及的关系是一种is - a的关系,并且需要实现代码的复用,那么就使用抽象类
    什么时候使用的接口——
    当所涉及的关系是一种has - a的关系,并且需要组合的使用和实现抽象的场景,那么就用接口

    抽象类是一种自下而上的设计思想,即只用子类需要然后才去考虑父类,
    接口是一种自上而下的设计思想,所有的子类都必须要实现接口中的所有的方法
    2019-11-22
    2
  • Dimple
    1. 目前学习的就是Java余元,所以有了一套线程的支持接口和抽象类的东西。其实刚入门的时候,确实对这两个不是很理解,而且很多时候都不知道如何运用,那时候是靠《疯狂Java讲义》学会了区别,并且通过一些面试题了解到其中的一些区别和使用方式。
    2. 通过今天老师的讲解,我又结合了《疯狂Java讲义》、面试题、以及自己项目中的代码实现,来重新梳理这两个的区别。为什么我的业务代码是使用接口而不是抽象类,为什么有些API是用了抽象类而不是接口,是基于某方面的考虑,是处于什么方式的考虑,或是基于哪个设计模式。不过这些都还需要消化,沉淀,会用只是一方面,会思考,会架构才是更好的方式。
    2019-11-20
    2
  • 编程界的小学生
    1.普通类模拟抽象类方法:
    私有构造器,protected修饰的方法和成员变量,模拟抽象方法的时候在实现体内直接抛出异常并写好交由子类具体实现的注释说明
    2.用的JAVA。天然支持,abstract和interface
    3.抽象类:
    不支持多继承,为了解决代码复用问题而产生,他可以内部定义方法实现、公用属性以及需要子类各自实现的抽象方法,应用场景最典型的就是模板模式,is a的关系。爸爸儿子的关系。
    接口:
    支持多继承,为了让代码更加解耦合,更加灵活而产生,他所定义的方法都是抽象的,has a的关系,好比一种协议,一种规范,应用场景贼广泛,比如各大设计模式,设计原则。再比如责任链模式
    2019-11-20
    3
    2
收起评论
99+
返回
顶部