面向对象-继承与接口

算是读书笔记吧

极客时间--设计模式之美


抽象类

抽象类实际上就是类,只不过是一种特殊的类,这种类不能被实例化为对象,只能被子类继承。和类一样,抽象类也表示一种 is-a 的关系。

  • 抽象类不允许被实例化,需要使用它的子类,并实现它的抽象方法

以一个上报系统为例,他分为TCP和HTTP两种上报方式:
抽象类中实现了可复用的主要逻辑(比如新增、格式化、存储、切割),子类中实现需要定制其关键步骤的抽象方法具体如何实现(比如TCP/HTTP发送)。

同样我们可以把上报的模块抽离出来,作为参数传递给manager进行不同的上报动作。这种方式又叫多态。

可以看出,抽象类(继承)的主要目的是为了代码的复用。

模板模式

模板模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

上面的上报模块,其实就是利用继承关系实现的模板模式代码。

模板模式与Callback

二者的目的很相似:复用和扩展

对于复用,二者都在主类(基类)中进行:
对于扩展,模板模式在子类实现,而Callback用闭包包装代码块实现

二者的区别在于Callback由于使用组合的方式,比继承更加灵活:
比如对于某些语法检查,模板模式要求子类必须实现所有的抽象方法。
而Callback则没有这个限制,只需要实现必须的Callback函数即可运行。


接口(协议)

接口表示一种 has-a 关系,表示具有某些功能。对于接口,有一个更加形象的叫法,那就是协议(contract)。

  • 接口只能声明方法,方法不能包含代码实现
  • 类实现接口的时候,必须实现接口中声明的所有方法

以公司为例:
每个参加工作的个体都有不同,可能继承自不同的父母。但是我们可以抽象出上班、打卡、996等等一系列协议。一旦一个类想要来工作,只要实现这一套协议,随时来公司修福报。

其实也可以用多态实现这一系列流程,但是相对于多态,协议更加解耦和灵活。
因为它对类没有父类的要求,只要实现了协议任何类都可以接入系统。一个类实现了多套协议,完全可以接入多个系统(比如我既要996,剩下一天还得带娃)。

可以看出接口主要在于解耦、扩展和灵活性。

抽象类与与接口的区别

  • 从设计上来讲
  1. 如果我们要表示一种 is-a 的关系,并且是为了解决代码复用的问题,我们就用抽象类。
  2. 如果我们要表示一种 has-a 关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。
  • 从实现流程上来讲
  1. 抽象类是一种自下而上的设计思路,先有子类的代码重复,然后再抽象成上层的父类(也就是抽象类)。
    首先你需要先实现一遍,才能知道哪里的逻辑重复。

  2. 接口是一种自上而下的设计思路。我们在编程的时候,一般都是先设计接口,再去考虑具体的实现。
    首先你要知道你想要什么,才能告诉业务方他需要提供什么。

为什么基于接口而非实现编程

封装不稳定的实现,暴露稳定的接口。
上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低耦合性,提高扩展性。

基于接口编程又叫“基于抽象编程”
越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。
好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。

具体到实际中,可以参考上面上报系统的例子。
TCP/HTTP两模块原本是通过抽象类实现的,现在把upload动作从抽象方法改为接口

public interface UpLoadInterface{ 
  void upload(Array arr); 
}

public class TCPUploadStore implements UpLoadInterface {
  //...省略属性、构造函数等...

  public void upload(Array arr) {
    //TCP上报...
  }
}

public class HTTPUploadStore implements UpLoadInterface {
  //...省略属性、构造函数等...

  public void upload(Array arr) {
    //http上报...
  }
}

// ReportManager的使用举例
public class ReportManager {
  //...省略其他无关代码...
  /***
  新增、格式化、存储、切割一系列通用方法...
  ***/

  public void uploadData() {  //上传数据
    Array dataArray = ...;//整理数据
    UpLoadInterface uploadStore = new TCPUploadStore(...);
    uploadStore.upload(dataArray); //进行上报
  }
}

结合之前关于抽象类的描述,我们的UpLoadInterface接口存在的意义是解耦upload功能逻辑。
使其可以灵活的扩展成HTTP,或者其他更多的upload方式

这里的灵活,指的正是通过定义接口的方式,让新的功能明确的知晓自己需要实现那些功能,以接入系统。


多用组合少用继承

一旦继承层次过深、过复杂,也会影响到代码的可维护性。在这种情况下,我们应该尽量少用,甚至不用继承。

  • 为什么不推荐使用继承?
    随着需求以及代码的迭代,原本只需要一两层就能解决的继承关系,可能会演变成宫斗剧一样的继承脉络。
1. 鸟
2. 会飞的鸟;不会飞的鸟
3. 会飞的会下蛋的;会飞的不会下蛋的;不会飞的会下蛋的;不会飞的不会下蛋的;

这样会导致代码的可读性变差,也破坏了类的封装特性(子类必须知道父类的具体实现,才能正确工作)。

  • 组合相比继承有哪些优势?
    继承主要有三个作用:表示 is-a 关系,支持多态特性,代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成。
    除此之外,利用组合还能解决层次过深、过复杂的继承关系影响代码可维护性的问题。

对于代码复用,这里可以举个例子:


public interface Flyable {
  void fly();
}
public class FlyAbility implements Flyable {
  @Override
  public void fly() { //... }
}
//省略Tweetable/TweetAbility/EggLayable/EggLayAbility

public class Ostrich implements Tweetable, EggLayable {//鸵鸟
  private TweetAbility tweetAbility = new TweetAbility(); //组合
  private EggLayAbility eggLayAbility = new EggLayAbility(); //组合
  //... 省略其他属性和方法...
  @Override
  public void tweet() {
    tweetAbility.tweet(); // 委托
  }
  @Override
  public void layEgg() {
    eggLayAbility.layEgg(); // 委托
  }
}
  • 如何判断该用组合还是继承?
    如果类之间的继承结构稳定,层次比较浅,关系不复杂,我们就可以大胆地使用继承。
    反之,我们就尽量使用组合来替代继承。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容