架构师训练营第二周笔记 - 带你认识框架设计原则和设计模式
摘要:本周主要学习抽象的重要性及面向对象三要素。再次基础之上学习四个框架设计原则开闭原则,里氏替换原则,单一原则,接口分离原则。在讲解开闭原则时通过提问和回答分别介绍了如何抽象继承进而引出了策略模式,适配器,观察模式。最后通过本周所学介绍反应式编程框架 Flower 的设计(这一节听的云里雾里,老师讲的时候视频中确实指向性互动,只能凭老师的语音的在视频画面上找),介绍如何设计框架。
编程语言的实质,
写程序十几年了却很少问编程的目的是什么?当然是用计算机来解决现实世界的问题,现在所流行的 AI,自动驾驶等等都是来解决现实世界的问题的。现实世界那么多问题,计算机甚至包含 AI 并不能全部解决,只能解决特定的问题,即使 AlphaGO 最初也只是解决了下围棋的具体步骤,它并不能观察人的表情推测人表情背后的思维。特定的问题是否可以称之为领域问答题。
现实世界和计算机不能划等号=,如何把现实世界映射到计算机中呢,这需要在两者之间建立一种联系,但是我们可以通过分析领域问题,对分析的结果进行抽象翻译成计算机可识别的符号。(或许这就是老师课程讲的技术背后的规律,看到某一技术时一定要看背后的规律而不是只了解表面皮毛。自己如何编程呢?从更高的角度多去分析抽象,不要沉迷在自我陶醉的细节中)
什么是面向对象编程
万物结为对象,面向对象编程有封装性(隐藏实现),继承性(接口重用),多态性(对象互换
目标:强内聚,低耦合
易扩展,更强壮,可移植,更简单
原则:蛋糕师傅们已经总结整理出了一套套做不同口味的方方法(设计模式)只有采用这些方法就能避免把蛋糕做成一坨 SHI.
有了做蛋糕的方法,如何快速做出更多的蛋糕呢?需要一套高标准的制作工具及模具,水果用标准的刀一刀下去形状各种各样,奶面包放到模具中一“刀”成行,做做蛋糕的师傅眼跟不用关系模具和刀具是用什么制作的。蛋糕师傅只管用即可。这工具就编程中常用的框架 Spring Tomcat 等。采用这些框架避免工作极大的简化了我们的工作。
好多时候蛋糕师傅做了一辈子蛋糕,但有的师傅就用这个做蛋糕的经验设计出来我的工具。这就像一个程序员写了一辈子的程序,也只是一个程序员,并没有成为你架构师。架构师是具备设计框架能力或是设计工具(框架)的那个人,设计框架也是架构师基本能力之一。
框架的核心
美味的蛋糕设计 vs 臭味 shi 的设计
好的设计一定是易扩展的健壮的可移植的还要简单。
相反错误的设计就是僵硬的,脆弱的,不可移植,晦涩难懂,过于复杂,设计过度。试想一下当你在修改一个小功能的时候,可能是改动一行代码,这时候涉及到四五个类的变动,这会让人崩溃的。更要命的只改一个小地方然后就引起系统崩溃了。这时候这个系统就是脆弱的,不健壮。当另一个地方想用这个模块的时候,没法拿出来直接使用,基本不可移植,一坨 SHI,怎么能把 SHI 底拿到别的地方去,想想都恶心。
程序设计中没有出现明显的好的结构设计,这显然就是过渡设计了。代码中如果大量重复的函数和结构那这就是重复设计了。代码及设计一定要清晰易读意图清晰,否则像 shi 一样晦涩难吃。
带你一步步揭开设计的面纱并亲吻它
在上一可老师带我们认识了一个糟糕的 button 设计。每个模式原则都是为了解决问题而诞生的。
通过此例子一步步按照软件原则重新设计,分别认识四个软件设计原则是开闭原则,依赖倒置原则,里氏替换原则,到最后的衷心职责(单一职责)接口。
开-闭原则 OCP- Open/Closed Principle
有一个男女朋友了,这时候交朋友是开放的,交男女朋友是关闭。
ocp 核心对扩展开放,对修改是关闭的,也就是说不修改软件实体而是对类,模块,函数进行扩展来实现新功能。
怎么做才不实现上述功能呢(增加新的按钮,不修改 Button)?
第一层面纱,关键是抽象,对抽象接口进行编程,当变更时是对接口的实现进行修改,抽象的接口和调用是不变的。
为了解决按钮存在的问题,1.对按钮进行抽象,定义一个父 button,分别让拨号 button 和发送 button 继承,这样两个按钮各自调用 Dailer 拨号功能,逻辑更清晰,不会出现勿调用。
但是依然存在问题 这两个按钮对 Dailer 有依赖,Button 难以移植和复用。
如何解决呢?策略模式出场啦!此刻需要掌声.
第二层面纱,策略模式:
我们定义一个策略接口 ButtonServer,让调用者(客户端)不再依赖具体对象,而是依赖我们定义的策略接口。我们依赖的对象去实现这个策略接口。
这样增加新功能时,只需要增加新的新的策略类实现策略接口即可。
现在 button 可以轻松移植了!
还有没有问题呢?还有现在具体 Dailer 还要实现策略类里面的方法。如果增加新的方法,需要在数字业务中用 if else 或 switch 去实现,这样的话 dailer 又变的僵硬脆弱了,不符合开闭原则。我们如何修改呢?
策略模式解决了 button 移植问题,带来了脆弱性,如何解决呢?适配器模式出场啦
第三层面纱,适配器模式:
既然在 Dailer 中增加 if else,switch 导致脆弱性,那就别让 Dailer 直接去实现 ButtonServer 接口,而是用其它的类去实现 ButtonServer 接口,在其中实现调用 Dailer 的接口。这样 Dailer 只对外暴露提供方法的样子。调用这些方法的类叫作 Adapter 。它把一个接口的调用是配成另一个接口和方法的调用。
用了适配器再增加新方法时只需要增加一个适配器即可。
此时此刻,Button 和 Dailer 都满足了开闭原则,增加新的按钮类型时不需要修改 Button 和 Dailer.
适配器有两个重要的角色适配器和被适配者,先记住后面老师肯定讲。哈哈
使用了适配器模式基本的设计目的就完成了,遵循了开闭原则,也消除了 Button 一开始设计的僵硬,脆弱,不可移植。但是有时候,我们需要灵活应用场景和需求,比如说增加一些操作,叫点亮按钮,这时候没有办法不修改代码完成扩展,怎么办?可以进一步的去修改按钮,观察者模式出场了。
第四层面纱,观察转模式
在这个模式里 Button 按钮按下的时候,不在是调用 ButtonServeer 的 ButtonPressed 方法,而是调用自己的 Listener-list 方法对象的方法,可以添加各种各样的 Listener,循环 List 执行 listener 里面的 buttonPressed.
需要增加新功能时需要新的功能只需要添加 listener 这个过程不需要修改代码实现了开闭原则。
名字很重要 Listener 观察者模式, Server 策略模式
依赖倒置 DIP 原则
既然是学习笔记先写结论:
DIP 原则高层模块不依赖底层模块,底层也不依赖高层模块,而是依赖一个抽象。但是抽象属于高层模块里面的,这是依赖倒置原则的精髓,抽象依赖高层模块才能依赖倒置。
DIP 是软件架构设计的指导原则,架构靠框架落地。
DIP 依赖倒置、框架编程技巧是架构师的核心技能
高层和实现都依赖抽象,这个抽象可以是规范,接口,还可能是配置。这个抽象不就是策略模式吗
案例中 Button 的 poll() 直接调用调用 Lamp 的 turnOn/turnOff 是违反了 DIP 原则,在中间增加一个 interface-buttonServe 抽象 r,使 button 依赖这个抽象。
找规律,找本质,不要只看表面
Button 的本质是什么
用什么机制检测用户的按键
目标对象是什么
不重要
里氏替换原则 Liskov
子类必须能够替换他们的基类
子类替换父类后程序若不能正常运行,则这个继承是不合理的
Liskow 是衡量继承合理不合理的重要原则
补充:OCP 开闭原则的关键是在于抽象,抽象的威力在于多态和继承(上一节课提到的,策略模式,适配器模式,观察者模式处处流淌着继承和多态的影子)。
课上老师用白马黑马继承了基类马的骑方法,当只有黑马和白马的时候这个场景是符合里氏原则的,一旦小马继承了马就违反了里氏原则,因为小马不能被人骑(别抬杠啊,小马小孩骑,那刚生下来不会走的马呢?能骑)。
白马黑马小马 is 马静态分析可以,但是用 LSP 原则动态分析就不对,所以分析的时候不是看它的状态、标识而是看对象的行为。所以类的区分应该用行为区分。
是从契约的角度来看里氏替换原则 LSP:
凡事要求使用基类的地方一定可以用其子类进行替换
Java 语法
子类一定拥有其基类的这个接口
子类的方控制不能笔记父类更严格
如何解决呢?
提取共性到基类
更好的办法是组合,减少必要的继承,使其成为成员变量而是用他
继承和组合都是 OOP 的两种扩展手段
继承有点:
比较容易,因为基类的大部分功能通过继承直接进入子类
继承确定:
破坏了封装,继承将基类的更多细节暴露给了子类,因而继承在称为“白盒复用”
当基类发生改变时,可能层层影响其下的子类
继承是静态的,无法再运行时改变组合
类的数量爆炸
如何中如何检测是否违反 LSP 呢
违反征兆:
当出现派生类的退化函数,即子类实现了父类的一个空函数,
单一接口职责隔离 SRP
相同的功能和职责聚集在一起
一个类只有一个引起其他变化的原因
违反 SRP 原则的后果
不可移植性
区分类的方法,分清职责
有时候类的职责并不清楚,有时候共享一些中间状态变量,如果强行分到不同的类中,反而会带来问题,如何解决呢? 接口分离原则出场了
接口分离原则 IPS
不应该强迫客户程序上依赖他们不需要的方法
ISP VS SRP
ISP 和 SRP 是相关的,都是和内聚性有关
SRP 强调如何设计一类,只有一种原因才能发生改变
老师用 modem 和胖接口(门定时关闭)例子来讲解。
定时门使用适配器模式解决的或多继承是来解决。
版权声明: 本文为 InfoQ 作者【郎哲158】的原创文章。
原文链接:【http://xie.infoq.cn/article/7adb50f40166c0ae69335cd5a】。未经作者许可,禁止转载。
评论