封面

版权信息

PREFACE前言

服务与支持

第1章 初探

|创建篇|

NOTE

在某个系统中只存在一个实例,同时提供集中、统一的访问接口,以使系统行为保持协调一致

2022-06-21 20:06:48

NOTE

singleton一词在逻辑学中指“有且仅有一个元素的集合”

2022-06-21 20:06:56

NOTE

“private”关键字确保太阳实例的私有性、不可见性和不可访问性;而“static”关键字确保太阳的静态性,将太阳放入内存里的静态区,在类加载的时候就初始化了,它与类同在,也就是说它是与类同时期且早于内存堆中的对象实例化的,该实例在内存中永生,内存垃圾收集器(Garbage Collector,GC)也不会对其进行回收;“final”关键字则确保这个太阳是常量、恒量,它是一颗终极的恒星,引用一旦被赋值就不能再修改;最后,“new”关键字初始化太阳类的静态实例,并赋予静态常量sun

2022-06-21 20:11:50

NOTE

“饿汉模式”(eager initialization)

2022-06-21 20:11:41

NOTE

无请求就不实例化,节省了内存空间

2022-06-21 21:27:54

NOTE

这样的程序逻辑看似没问题,但其实在多线程模式下是有缺陷的。试想如果是并发请求的话,程序第10行的判空逻辑就会同时成立,这样就会多次实例化太阳,并且对sun进行多次赋值(覆盖)操作,这违背了单例的理念

2022-06-21 21:28:09

NOTE

相比“懒汉模式”,其实在大多数情况下我们通常会更多地使用“饿汉模式”,原因在于这个单例迟早是要被实例化占用内存的,延迟懒加载的意义并不大,加锁解锁反而是一种资源浪费,同步更是会降低CPU的利用率,使用不当的话反而会带来不必要的风险

2022-06-21 21:31:51

第3章 原型

NOTE

类的实例化与克隆之间的区别,二者都是在造对象,但方法绝对是不同的。原型模式的目的是从原型实例克隆出新的实例,对于那些有非常复杂的初始化过程的对象或者是需要耗费大量资源的情况,原型模式是更好的选择

2022-08-28 01:38:38

第4章 工厂方法

NOTE

工厂方法模式不但能将客户端与敌人的实例化过程彻底解耦,抽象化、多态化后的工厂还能让我们更自由灵活地制造出独特而多样的产品。

2022-08-30 00:17:17

第5章 抽象工厂

NOTE

但是系统如果按工厂方法那样为每种产品都增加一个新工厂又会造成工厂泛滥。所以,为了调和这种矛盾,抽象工厂模式提供了另一种思路,将各种产品分门别类,基于此来规划各种工厂的制造接口,最终确立产品制造的顶级规范,使其与具体产品彻底脱钩

2022-06-21 21:40:11

NOTE

需要注意的是,抽象工厂模式一定是基于产品的族系划分来布局的,其产品系列一定是相对固定的,故以抽象工厂来确立工业制造标准(各产品系列生产接口)。而产品族则可以相对灵活多变,如此一来,我们就可以方便地扩展与替换族工厂,以达到灵活产出各类产品族系的目的。

2022-08-30 00:20:06

第6章 建造者

|结构篇|

第8章 组合

NOTE

我们可以用组合模式来表达“部分/整体”的层次结构,提取并抽象其相同的部分,特殊化其不同的部分,以提高系统的可复用性与可扩展性,最终达到以不变应万变的目的。

2022-06-23 13:22:53

NOTE

但要体现出组合模式的优势还在于如何运用这个树结构

2022-06-23 13:33:12

NOTE

接下来的文件夹类就比较特殊了,它不仅要先输出自己的名字,还要换行再逐个输出子节点的名字,并且要保证空格逐级递增

2022-06-23 14:28:28

NOTE

空格偏移量这个必传参数可能让用户非常困惑,或许我们可以为抽象节点类添加一个无参的展示方法“tree()”,在其内部调用“tree(0)”,如此一来就不再需要用户传入偏移量了,使用起来更加方便

2022-06-23 14:30:49

NOTE

组合模式将树形结构的特点发挥得淋漓尽致,作为最高层级抽象的抽象节点类(接口)泛化了所有节点类,使任何“整体”或“部分”达成统一,枝(根)节点与叶节点的多态化实现以及组合关系进一步勾勒出的树形结构,最终使用户操作一触即发,由“根”到“枝”再到“叶”,逐级递归,自动生成。我们来看组合模式的类结构,如图8-7所示。

2022-08-30 00:24:10

第9章 装饰器

第10章 适配器

NOTE

实现接口的匹配

2022-06-23 15:10:00

NOTE

类适配器

2022-06-23 15:09:58

NOTE

对象适配器

2022-06-23 15:09:56

NOTE

当然,事物没有绝对的好与坏,对象适配器与类适配器各有各的适用场景。假如我们只需要匹配电视机这一种设备,并且未来也没有任何其他的设备扩展需求,那么类适配器使用起来可能更加简便,所以具体用什么、怎么用还要视具体情况而定,切不要有过分偏执、非黑即白的思想。

2022-06-23 15:20:52

NOTE

适配器模式让兼容性问题在不必修改任何代码的情况下得以解决,其中适配器类是核心

2022-06-23 15:20:46

第11章 享元

NOTE

虽然马赛克小块数量比较多,但经过观察我们会发现,归类后只有4种:黑色块、灰色块、灰白色块以及白色块。我们可以说,这就是4个“元”色块

2022-06-23 15:26:52

NOTE

我们可以发现整张游戏地图都是由一个个小的单元图块组成的,其中除房屋比较大之外,其他图块的尺寸都一样,它们分别为河流、草地、道路,这些图块便是4个元图块

2022-06-23 15:27:31

NOTE

我们完全可以把相同的图块对象共享,用克隆的方式来省去实例化的过程,从而加快初始化速度

2022-06-23 15:31:50

NOTE

然而,对这几个图块克隆貌似没什么问题,地图加载速度确实提高了,但是构建巨大的地图一定会在内存中产生庞大的图块对象群,从而导致大量的内存开销。如果没有内存回收机制,甚至会造成内存溢出,系统崩溃

2022-06-23 15:32:00

NOTE

简单讲就是被绘制出来后就不必变动了,即使要变也是将拼好的地图作为一个大对象整体挪动。图块一旦被绘制出来就不需要保留任何坐标状态,内存中自然也就不需要保留大量的图块对象了

2022-06-23 15:32:24

NOTE

材质图是可以作为享元的,而坐标则不能

2022-06-23 15:33:16

NOTE

既然要共享相同的图片,那么我们就得将图块类按图片拆分成更细的材质类,如河流类、草地类、道路类等。而坐标不能作为图块类的享元属性,所以我们就得设法把这个属性抽离出去由外部负责

2022-06-23 15:33:28

NOTE

河流类中只定义了图片作为内部属性。在第6行的类构造器中加载河流图片,这就是类内部即将共享的“元”数据了,我们通常称之为“内蕴状态”。而作为“外蕴状态”的坐标是无法作为享元的,所以将其作为参数由第11行实现的绘图方法中由外部传入

2022-06-23 15:35:32

NOTE

调用后会在地板图层之上绘制房屋,覆盖下面的地板(房屋图片比其他图片要大一些),以使地图变得更加立体化

2022-06-23 15:38:31

NOTE

“元之共享”的关键了,我们得定义一个图件工厂类,并将各种图件对象提前放入内存中共享,如此便可以避免每次从磁盘重新加载

2022-06-23 15:38:42

NOTE

首先在第5行的构造方法中初始化一个散列图的“缓存池”,然后通过懒加载模式来维护它

2022-06-23 15:40:44

NOTE

无论外部需要什么图件,也无论外部获取多少次图件,每类图件都只会在内存中被加载一次,这便是“元共享”的秘密所在

2022-06-23 15:40:57

NOTE

Flyweight(享元接口):所有元件的高层规范,声明与外蕴状态互动的接口标准。对应本章例程中的绘图接口Drawable。ConcreteFlyweight(享元实现):享元接口的元件实现类,自身维护着内蕴状态,且能接受并响应外蕴状态,可以有多个实现,一个享元对象可以被称作一个“元”。对应本章例程中的河流类River、草地类Grass、道路类Road等。FlyweightFactory(享元工厂):用来维护享元对象的工厂,负责对享元对象实例进行创建与管理,并对外提供获取享元对象的服务。Client(客户端):享元的使用者,负责维护外蕴状态。对应本章例程中的图件工厂类TileFactory。

2022-06-23 15:43:35

NOTE

与中国古代先哲们对“阴阳”二元的思考类似,“享元”的理念其实就是萃取事物的本质,将对象的内蕴状态与外蕴状态剥离开来,其中内蕴状态成为真正的“元”数据,而外蕴状态则被抽离出去由外部负责维护,最终达成内外相济、里应外合的结构,使元得以共享。大千世界,万物苍生,究其“元”,万变不离其宗,宜“享”之。

2022-06-23 15:43:22

第12章 代理

第13章 桥接

NOTE

抽象与实现分离,使二者可以各自单独变化而不受对方约束

2022-06-23 15:50:28

NOTE

降低了抽象与实现这两个可变维度的耦合度,以保证系统的可扩展性

2022-06-23 15:50:35

NOTE

颜色的多态

2022-06-23 15:53:57

NOTE

形状的抽离

2022-06-23 15:54:00

NOTE

形状可以依赖尺子来规范其笔触线条走向

2022-06-23 15:54:11

NOTE

经济发展靠分工,系统扩展靠抽离,桥接模式将抽象与实现彻底解耦,使形状与颜色的纠葛终被化解,各自为营,互不侵扰。劳动分工实现了各种产品制造的自由扩展,使其能够在各自维度上达成多态,无限延伸

2022-08-30 00:27:17

|行为篇|

第15章 迭代器

NOTE

迭代器模式(Iterator)提供了一种机制来按顺序访问集合中的各元素,而不需要知道集合内部的构造

2022-06-23 16:14:36

NOTE

针对不同的集合类的迭代方式也不尽相同

2022-06-23 18:04:13

NOTE

Java8引入的流式遍历

2022-06-23 18:04:18

NOTE

Set本身集合不存在index索引号,所以必须用foreach循环(迭代器Iterator)进行遍历

2022-06-23 18:04:31

NOTE

一种通用的迭代标准,能让调用者使用统一的方式进行遍历吗?如果我们深究源码就会发现,Collection接口中有这样一个接口

2022-06-23 18:04:46

NOTE

返回标准的迭代器对象。既然有了这种标准,那么Collection这集合类就可以实现自己的迭代器

2022-06-23 18:05:11

NOTE

Map集合也可以按照同样的方式,从其EntrySet中获取迭代器

2022-06-23 18:05:17

NOTE

作为一种拥有特殊数据结构的集合,弹夹可以容纳多颗子弹。向弹夹内装填子弹与压栈操作非常类似,而射击则类似于出栈操作。首先要弹出最后一次装填的子弹,子弹发射后再弹出下一颗子弹,直到弹出装填的第一颗子弹为止,最后弹夹被清空,遍历结束。这与栈集合“先进后出,后进先出”的数据结构如出一辙。

2022-06-23 18:20:05

NOTE

如此简单粗暴的做法确实能达到目的,但是这会严重破坏行车记录仪的数据逻辑封装。用户对索引位置等内部状态信息一无所知,也不会进行维护,如果用户随意对数据进行增加或删除就会导致索引位置错乱,再继续记录很可能会覆盖最新、最重要的视频信息,导致用户数据安全无法得到保证。

2022-06-23 18:32:43

NOTE

定义迭代器是有必要的,如此我们不但可以避免用户随意操作而导致的内部逻辑混乱,还能提供给用户更方便、统一的数据遍历接口

2022-06-23 18:32:57

NOTE

此处我们做了适度简化,当然直接使用Java工具包util中自带的Iterator接口也是可以的

2022-06-23 18:33:23

NOTE

我们让它实现JDK提供的接口Iterable(代码比较简单,我们就不亲自写了,读者可以自行查看源码)

2022-06-23 18:33:35

NOTE

这样就能使迭代器轻松访问行车记录仪的私有数据集,并同时达到了迭代器与集合分离的目的。迭代器实现的重点在于第21行定义的迭代器游标cursor,我们将其初始化为集合索引的位置,二者相对独立,自此再无瓜葛

2022-06-23 18:51:32

第16章 责任链

NOTE

财务专员(1 000元审批权限)、财务经理(5 000元审批权限)以及财务总监(10 000元审批权限)

2022-06-24 13:37:53

NOTE

然而这种办事效率确实不敢恭维,申请人先找专员被升级处理,再找经理又被告知数额过大得去找总监,来来回回找了3个审批人处理,浪费了申请人的大量时间与精力。虽然事情是办理了,但申请人非常不满意,审批流程太过烦琐,总觉得有种被踢皮球的感觉。

2022-06-24 13:45:44

NOTE

架构工作流

2022-06-24 13:52:27

NOTE

缺少架构的流程不是完备的工作流,否则申请人终将被淹没在一堆复杂的审批流程中

2022-06-24 13:52:40

NOTE

现审批人的业务之间有环环相扣的关联,对于超出审批人职权范围的申请会传递给上级,直到解决问题为止。这种传递机制就需要我们搭建一个链式结构的工作流,这也是责任链模式的精髓之所在

2022-06-24 13:52:51

NOTE

每位审批人的角色不同,其审批逻辑也有所区别

2022-06-24 13:54:29

第17章 策略

第18章 状态

NOTE

二元态

2022-06-24 13:14:53

NOTE

如果客户端连续按下开或者关按钮会出现什么情况呢?实际上这即使没有逻辑错误也增加了无意义的冗余操作,已经点亮的灯又何必再次被开启呢?所以这个开关类的状态校验很不完善,我们需要加入针对当前状态的条件判断,也就是说,开启的状态下不能再开启,关闭的状态下不能再关闭

2022-06-24 13:16:16

NOTE

然而非常遗憾的是,这依旧不算是好的设计,如果状态再复杂些,逻辑判断就会越加越多

2022-06-24 13:17:44

NOTE

复杂的状态校验逻辑会大量堆积在每个方法中,因此造成的错误必将导致严重的交通事故,后果不堪设想

2022-06-24 13:18:09

NOTE

若是东西南北各处交通灯全部联动起来的话,其复杂程度难以想象

2022-06-24 13:20:41

NOTE

非常神奇的是,我们看不到任何的切换逻辑了,之前代码中的一大堆if、else全都消失不见了

2022-06-24 13:25:04

NOTE

通过对交通灯系统的初步重构,我们将“状态”接口化、模块化,最终将它们从臃肿的交通灯类代码中抽离出来,独立于交通灯类,并分别拥有自己的接口实现。如此一来,我们奇迹般地摆脱了各种复杂的状态切换逻辑,代码变得特别清爽、优雅

2022-06-24 13:25:45

NOTE

状态切换逻辑已经被拆分出去了,交通灯类变得非常简单。

2022-06-24 13:27:02

NOTE

也就是说,交通灯只是持有当前的状态,至于到底该如何响应及进行状态切换,全权交由当前状态对象处理

2022-06-24 13:27:43

第19章 备忘录

NOTE

Java对象(Plain Ordinary Java Object, POJO)类

2022-06-23 18:59:43

NOTE

编辑器类提供的删除方法本来是出于软件功能的完整性而设计的,却反而给用户带来了潜在风险。所以,我们一定要避免发生这类误操作,才能带来更好的用户体验

2022-06-23 19:22:23

NOTE

这种自动备忘录机制是如何实现的呢?既然可以回溯历史,就一定得定义一个历史快照类,用来记录用户每步操作后的文档状态,请参看代码清单19-4。

2022-06-23 19:22:32

第20章 中介

NOTE

为对象构架出一个互动平台,通过减少对象间的依赖程度以达到解耦的目的

2022-06-23 20:35:34

NOTE

对媒体来说,虽然它们的作用都一样,但在传递信息的方式上还是有差别的。如图20-1所示,以传统媒体为例,书刊杂志、报纸、电视、广播等,都是把信息传递给读者,有些是实时的(如电视),有些是延迟的(如报纸),但它们都是以单向的传递方式来传递信息的。而作为新媒体的互联网,不但可以更高效地把信息传递给用户,而且可以反向地获取用户的反馈信息

2022-06-23 20:35:55

NOTE

对象间这种千丝万缕的耦合关系会带来很大的麻烦,当我们要加入或减少一个参会人时,都要将其同步更新给所有人,每个人发送消息时都要先查找一遍消息接收方,从而产生很多重复工作。我们陷入了一种多对多的对象关联陷阱,这让复杂的对象关系难以维护,所以必须重新考虑更合理的设计模式

2022-06-23 21:21:10

NOTE

聊天室中介平台的搭建,让用户以一种间接的方式进行沟通,彻底从错综复杂的用户直接关联中解脱出来

2022-06-24 10:12:19

NOTE

多态化沟通

2022-06-24 10:12:22

NOTE

中介模式不仅在生活中应用广泛,还大量存在于软硬件架构中,例如微服务架构中的注册发现中心、数据库中的外键关系表,再如网络设备中的路由器等,中介的角色均发挥了使对象解耦的关键作用。不管是对象引用维护还是消息的转发,都由处于中心节点的中介全权负责,最终架构出一套类似于星形拓扑的网络结构

2022-06-24 12:31:31

NOTE

极大地简化了各对象间多对多的复杂关联,最终解决了对象间过度耦合、频繁交互的问题,请参看中介模式的类结构

2022-06-24 12:31:37

第21章 命令

NOTE

第3行中我们声明了灯泡类的引用,并于第5行的构造方法中将其初始化,那么无疑这里的开关与灯泡就绑定了,也就是说开关与灯泡强耦合了

2022-06-24 14:17:14

NOTE

有些读者可能意识到了这里应该使用策略模式,用接口来承接灯泡或者其他类电器,以此来解决耦合问题。当然,这样可以使设备端与控制器端解耦,但控制器与设备接口又耦合在一起了,简单说就是控制器只能控制某一类接口的设备,依然存在一定的局限性

2022-06-24 14:17:54

NOTE

由于场景比较简单,我们将所有命令简化为一个类来实现了。其实更确切的做法是将每个命令封装为一个类,也就是可以进一步将其拆分为“开命令”(OnCommand)与“关命令”(OffCommand)两个实现类,其中前者的执行方法中触发灯泡的开灯操作,反向执行方法中则触发灯泡的关灯操作,而后者则反之,以此支持更多高级功能。

2022-06-24 14:25:51

NOTE

命令模块已经就绪,并成功与命令执行方(灯泡)对接,这时作为命令请求方的开关就彻底与灯泡解耦了,也就是说,开关不能直接控制电灯了。我们来看如何对之前的开关类Switcher进行重构

2022-06-24 14:29:15

NOTE

开关类Switcher不再引入任何灯泡对象,取而代之的是第3行持有的命令接口Command

2022-06-24 14:30:40

NOTE

命令模块以接口以及实现类的方式被成功地植入开关控制器芯片。最后我们来看如何将这些模块组织起来

2022-06-24 14:30:47

NOTE

此处的代码逻辑不是重点,读者更需要关注的是这个闪烁命令同样符合命令接口Command的标准,如此才能保证良好的系统兼容性,并成功植入开关控制器芯片(命令请求方)完成事件与命令的绑定

2022-06-24 14:32:30

第22章 访问者

NOTE

访问者模式(Visitor)主要解决的是数据与算法的耦合问题,尤其是在数据结构比较稳定,而算法多变的情况下。为了不“污染”数据本身,访问者模式会将多种算法独立归类,并在访问数据时根据数据类型自动切换到对应的算法,实现数据的自动响应机制,并且确保算法的自由扩展。

2022-06-24 14:48:18

NOTE

对数据的封装,我们常常会用到POJO类,它除get和set方法之外不应包含任何业务逻辑,也就是说它只封装了一组数据且不具备任何数据处理能力,最常见的如做OR-Mapping时数据库表所对应的持久化对象(Persistent Object,PO)或转换后的值对象(Value Object,VO)

2022-06-24 14:48:31

NOTE

水果类Fruit则有些特殊,因为它是散装出售并且按斤计价的,所以单品对象的价格不固定,我们为其增加了一个重量属性weight

2022-06-24 14:52:39

NOTE

对于生产日期越早的商品打折力度越大,而过期商品则不能出售,但这种计价策略不适用于酒类商品。针对不同商品的优惠计价策略是不一样的,作为访问者的收银员应该针对不同的商品应用不同的计价方法

2022-06-24 14:53:22

NOTE

instanceof

2022-06-24 14:53:35

NOTE

我们已经利用访问者的重载方法实现了计价方法的自动派发机制

2022-06-24 15:00:33

NOTE

访问者的重载方法只能对单个“具体”商品类进行计价,当顾客推着装有多件商品的购物车来结账时,“含糊不清”的“泛型”商品可能会引起重载方法的派发问题。实践出真知,我们用之前的访问者来做一个清空购物车的实验

2022-06-24 15:01:59

NOTE

重载方法自动派发不能再正常工作了,这是由于编译器对泛型化的商品类Product茫然无措,分不清到底是糖果还是酒,所以也就无法确定应该调用哪个重载方法了

2022-06-24 15:03:10

NOTE

如何解决访问者对泛型化的商品类的自动识别、分拣是当下最关键的问题。

2022-06-24 15:03:32

NOTE

试想,如果我们给购物车里新加了一个蔬菜类Vegetable,但没有在Visitor里加入其重载方法visit(Vegetable vegetable),那么运行时到底应该派发给哪个重载方法呢?运行时出错岂不是更糟糕?这就是编译器会提前报错,以避免更严重问题的原因

2022-06-24 15:04:08

NOTE

访问与接待

2022-06-24 15:04:13

NOTE

基于这种“主动亮明身份”的理念,我们对系统进行重构,之前定义的商品模块就需要作为“接待者”主动告知“访问者”自己的身份,所以它们要一定拥有“接待排查”的能力。我们定义一个接待者接口来统一这个行为标准

2022-06-24 15:04:42

NOTE

糖果类Candy实现接待者接口Acceptable,顺理成章地成为了“接待者”,并在第9行主动把自己(this)交给了访问者以亮明身份。注意此处的“this”明确了自己的身份属于糖果类Candy实例,而绝非任何泛型类

2022-06-24 15:08:14

第23章 观察者

NOTE

当目标的状态在没有发生变化的情况下,观察者依旧在进行观察,交互效率非常低,这正类似于利用HTTP协议对服务器对象状态发起的轮询操作(Polling)

2022-06-24 16:00:39

NOTE

由于HTTP无状态连接协议的特性,服务端无法主动推送(Push)消息给Web客户端,因此我们常常会用到轮询策略,也就是持续轮番询问服务端状态有无更新。然而当访问高峰期来临时,成千上万的客户端(观察者)轮询会让服务端(被观察者)不堪重负,最终造成服务端瘫痪

2022-06-24 16:00:51

NOTE

商店类其实就是一个POJO类,此处主要作为被观察的目标主题

2022-06-24 16:04:31

NOTE

然而,故事发展到这里也许并没有结束,前三位买家或许依旧在继续他们的抢购行为,买家大量的精力被这种糟糕的软件设计耗费了

2022-06-24 16:06:28

NOTE

这种以观察者为主动方的设计缺陷,大量无用功被消耗在状态交互上

2022-06-24 16:06:39

NOTE

与其让买家们无休止地询问,不如在到货时让商店主动通知买家前来购买。这种设计正类似于Websocket协议的交互方式,与23.1节中HTTP的轮询方式恰恰相反,它允许服务端主动推送消息给客户端

2022-06-24 16:06:51

NOTE

这种反客为主的设计只需要一次握手协议并建立连接通道即可完成,之后发生的状态更新完全可以由服务端(被观察者)向Web客户端(观察者)进行消息推送的方式完成,这时就不会再有频繁轮询的情况发生了,交互效率问题迎刃而解

2022-06-24 16:07:02

第24章 解释器

NOTE

它是可以包含任意其他子指令的指令集,所以它是非终极表达式

2022-06-24 16:17:57

NOTE

其他的指令都应该是不可以再拆分的指令了,也就是说它们都是终极表达式

2022-06-24 16:18:04

NOTE

任何脚本都是由表达式组合起来的一棵语义树,通过这棵“树”,每个指令间的结构关系一目了然

2022-06-24 16:17:03

NOTE

没错,这就是本书第8章的“组合模式”,我们正是利用了“组合模式”的结构模型构建了语义树(Syntax Tree)以完成语言翻译工作。当然,组合模式强调的是数据组合的结构,而本章主要关注的是解释行为的抽象与多态

2022-06-24 16:17:14

NOTE

以提供给所有表达式一个统一的接口标准

2022-06-24 16:20:36

NOTE

具体情况还需具体分析

2022-06-24 16:20:42

NOTE

所有终极表达式至此完成

2022-06-24 16:23:14

NOTE

我们将它们按一定顺序组合起来就是非终极表达式了

2022-06-24 16:23:49

NOTE

并在第11行的解释方法interpret()中先后调用它们的解释方法,使解释工作延续到子表达式里去

2022-06-24 16:25:30

第25章 终道

NOTE

设计模式是以语言特性(面向对象三大特性)为“硬件基础”,再加上软件设计原则的“灵魂”而总结出的一系列软件模式

2022-06-24 16:40:44

NOTE

在所有禽类出现的地方无法用鸵鸟进行替换,这便违反了里氏替换原则

2022-06-24 16:46:22

NOTE

我们意识到最初的设计是有问题的,因为“禽类”与“飞翔”并无必然关系,所以对于禽类不应该定义飞翔方法fly()

2022-06-24 16:46:44

NOTE

我们对高层抽象进行重构,把禽类的飞翔方法fly()抽离出去并单独定义一个飞翔接口Flyable,对于有飞翔能力的鸟儿可以继承禽类并同时实现飞翔接口,而对于鸵鸟则依然继承禽类,但不用去实现飞翔接口

2022-06-24 16:46:58

NOTE

客户端对类的依赖基于最小接口,而不依赖不需要的接口

2022-06-24 16:47:35

NOTE

就是切勿将接口定义成全能型的,否则实现类就必须神通广大,这样便丧失了子类实现的灵活性,降低了系统的向下兼容性

2022-06-24 16:47:52

NOTE

动物”接口定义的行为过于宽泛,它们应该被拆分开来,独立为“可移动的”与“可发声的”两个接口

2022-06-24 16:48:27

NOTE

如此细分的接口设计便能让子类达到灵活匹配的目的

2022-06-24 16:48:38

NOTE

对接口尽可能地细粒度化,拆分开的接口总比整合的接口灵活

2022-06-24 16:48:47

NOTE

高层模块不依赖底层模块,也就是说高层模块只依赖上层抽象,而不直接依赖具体的底层实现,从而达到降低耦合的目的

2022-06-24 16:50:01

NOTE

让模块间的通信趋向“简单化”“傻瓜化”

2022-06-24 17:08:03

NOTE

设计模式绝不可以被滥用,以免陷入“为了设计而设计”的误区,导致过度设计

2022-06-24 17:08:26