软件设计之美
郑晔
推文科技技术VP,前火币网首席架构师
立即订阅
3457 人已学习
课程目录
已完结 36 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 软件设计,应对需求规模的“算法”
免费
课前必读 (3讲)
01 | 软件设计到底是什么?
02 | 分离关注点:软件设计至关重要的第一步
03 | 可测试性: 一个影响软件设计的重要因素
了解一个软件的设计 (4讲)
04 | 三步走:如何了解一个软件的设计?
05 | Spring DI容器:如何分析一个软件的模型?
06 | Ruby on Rails:如何分析一个软件的接口?
07 | Kafka:如何分析一个软件的实现?
设计一个软件—程序设计语言 (5讲)
08 | 语言的模型:如何打破单一语言局限,让设计更好地落地?
09 | 语言的接口:语法和程序库,软件设计的发力点
10 | 语言的实现:运行时,软件设计的地基
11 | DSL:你也可以设计一门自己的语言
加餐 | 再八卦几门语言!
设计一个软件—编程范式 (9讲)
12 | 编程范式:明明写的是Java,为什么被人说成了C代码?
13 | 结构化编程:为什么做设计时仅有结构化编程是不够的?
14 | 面向对象之封装:怎样的封装才算是高内聚?
15 | 面向对象之继承:继承是代码复用的合理方式吗?
16 | 面向对象之多态:为什么“稀疏平常”的多态,是软件设计的大杀器?
17 | 函数式编程:不用函数式编程语言,怎么写函数式的程序?
18 | 函数式编程之组合性:函数式编程为什么如此吸引人?
19 | 函数式编程之不变性:怎样保证我的代码不会被别人破坏?
加餐 | 函数式编程拾遗
设计一个软件—设计原则与模式 (7讲)
20 | 单一职责原则:你的模块到底为谁负责?
21 | 开放封闭原则:不改代码怎么写新功能?
22 | Liskov替换原则:用了继承,子类就设计对了吗?
23 | 接口隔离原则:接口里的方法,你都用得到吗?
24 | 依赖倒置原则:高层代码和底层代码,到底谁该依赖谁?
25 | 设计模式:每一种都是一个特定问题的解决方案
26 | 简单设计:难道一开始就要把设计做复杂吗?
设计一个软件—设计方法 (3讲)
27 | 领域驱动设计:如何从零开始设计一个软件?
28 | 战略设计:如何划分系统的模块?
29 | 战术设计:如何像写故事一样找出模型?
巩固篇 (3讲)
30 | 程序库的设计:Moco是如何解决集成问题的?
31 | 应用的设计:如何设计一个数据采集平台?
32 | 应用的改进:如何改进我们的软件设计?
结束语 (1讲)
结束语|那些没讲的事儿
软件设计之美
15
15
1.0x
00:00/00:00
登录|注册

22 | Liskov替换原则:用了继承,子类就设计对了吗?

郑晔 2020-07-17
你好!我是郑晔。
上一讲,我们讲了开放封闭原则,想要让系统符合开放封闭原则,最重要的就是我们要构建起相应的扩展模型,所以,我们要面向接口编程。
而大部分的面向接口编程要依赖于继承实现,虽然我们在前面的课程中说过,继承的重要性不如封装和多态,但在大部分面向对象程序设计语言中,继承却是构建一个对象体系的重要组成部分。
理论上,在定义了接口之后,我们就可以把继承这个接口的类完美地嵌入到我们设计好的体系之中。然而,用了继承,子类就一定设计对了吗?事情可能并没有这么简单。
新的类虽然在语法上声明了一个接口,形成了一个继承关系,但我们要想让这个子类真正地扮演起这个接口的角色,还需要有一个好的继承指导原则。
所以,这一讲,我们就来看看可以把继承体系设计好的设计原则:Liskov 替换法则。

Liskov 替换原则

2008 年,图灵奖授予 Barbara Liskov,表彰她在程序设计语言和系统设计方法方面的卓越工作。她在设计领域影响最深远的就是以她名字命名的 Liskov 替换原则(Liskov substitution principle,简称 LSP)。
1988 年,Barbara Liskov 在描述如何定义子类型时写下这样一段话:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《软件设计之美》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥19.9
立即订阅
登录 后留言

精选留言(11)

  • 赵冲
    那从父类的角度来考虑的话,应该是定义一个几何图形的接口,接口有计算面积的方法。然后长方形、正方形、圆形、三角形……都实现这个接口,然后各自实现计算面积的方法。各自有自己特别的关键属性,根据属性计算各自面积:长*宽、边长²、πr²、(底长*高)/2、……

    作者回复: 嗯,这个解决方案的味道不错。

    2020-07-17
    8
  • Being
    全篇一直在强调行为,我想这也是思考题的突破口。长宽是数据,而Rectangle并没有将行为抽象出来,导致Rectangle和Square不能成为IS-A的关系,我们只要把求面积的行为放在Rectangle下,子类分别去实现面积的方法就好了。

    作者回复: “把求面积的行为放在Rectangle下,子类分别去实现面积的方法”,可以解决这个问题吗?

    2020-07-18
    1
    3
  • Geek_3b1096
    时刻提醒自己: 千万要遏制写if的念头

    作者回复: 非常好的经验总结!

    2020-08-09
    1
  • 桃子-夏勇杰
    这个设计原则看着非常简单,提出者居然能获得图灵奖,可见这个设计原则的价值非常大。郑老师,这个设计原则的价值到底有多大呢?

    作者回复: LSP告诉我们什么样的继承是对的,而继承使用范围太广了。

    2020-07-29
    1
  • 三生
    所有的形状都有求面积的方式,但是计算方式都不同,这行为应该是“正常的”,但是设置长和宽的行为不正确,因为长方体有宽和高,正方形只有宽或高,这里只能抽象出计算面积这个方法。

    比如企鹅和麻雀,我们认为所有的鸟都会飞,但企鹅不会飞,而他却具有了飞的行为,这是“不正常”的
    2020-07-22
    1
  • monalisali
    RequestParser 中还是免不了用多个 if 来判断 identifier,从而返回特定的子类吧

    作者回复: 不一定,可以通过一个Map实现。

    2020-07-20
    1
    1
  • Jxin
    如果业务场景合适,约束功能也不失为一个解决办法。让宽高不可变,初始化时就必须赋值。这样就能符合现实中的特性。自然也没有长宽赋不同值的麻烦。

    作者回复: setter 确实是一个有杀伤力的东西,但回避 setter并不是在解决我们提出的问题。

    2020-07-17
    1
  • 阳仔
    Liskov替换的意思是子类型能够替换父类型,且在继承体系中保持接口的一致
    长方形与正方形计算面积的行为接口是一样的,但是定义长方形和正方形的接口是不一样的,所以这两个行为可以分别抽离出来

    作者回复: 长方形和正方形接口不一样,这是一个点。

    2020-07-17
    1
  • Michael
    我想请教一下老师 当我们可以抽象出统一的接口之后应该怎么处理参数的不同?举个例子:我现在又两种不同的上传文件的策略:1.上传S3; 2.上传到自己的document-service。这两种上传的策略在参数方面略有不同, 比如上传到S3的实现可能需要指定fileName, 上传到自己的document-service可能需要加上一些业务的字段,但是也有相同的参数, 比如最基本的二进制文件。所以按照我的理解我们应该有一个BaseUploadDocumentRequest作为父类,S3UploadDocumentRequest extends BaseUploadDocumentRequest, DocumentServiceUploadDocumentRequest extends BaseUploadDocumentRequest,同样的对于返回值也应该有同样的继承体系: S3UploadDocumentResponse extends BaseUploadDocumentResponse, DocumentServiceUploadDocumentResponse extends BaseUploadDocumentResponse。
    接口应该长这样:
    public interface DocumentUploadStrategy<RequestType extends BaseUploadDocumentRequest, ResponseType extends BaseUploadDocumentResponse> {
            public ResponseType uploadDocument(RequestType request);
    }

    请问老师我这样设计合理么?
    2020-07-31
  • 张驾驭
    在正方形类中重写长方形的计算面积接口

    作者回复: 但测试依然无法通过。

    2020-07-20
  • zchq88
    是不是可以吧Square做父类,然后Rectangle作为子类来设计,父类只有一个设置边长,子类增加设置长宽的接口,如果没有设置长宽默认使用边长的值。这样是不是符合替换原则?
    2020-07-20
收起评论
11
返回
顶部