架构训练营——第二周学习总结
编程的由来
最早的计算机编程非常麻烦,程序员需要将电线编来编去,作为输入数据,以控制计算机的执行
编程的本质
编程的目的:用计算机来解决现实世界中的问题
编程的过程:用计算机所能理解的模型(解空间)和现实世界(问题空间)之间,建立一种联系
编程语言本质是一种抽象机制:领域问题 -> 模型 -> 软件系统
问题领域 Problem Domain
包含与系统所要解决的问题相关的实物和概念的空间
抽象的种类
对机器本身进行抽象:机器码/汇编语言
对计算处理逻辑进行抽象:传统非结构化的高级语言(如 Basic、Fortran 等)
对问题领域进行一定程度的抽象:结构化的程序设计
直接表达问题空间内的元素:面向对象的程序设计
编程的核心要素
程序员——劳动者
计算机——劳动工具
客观问题领域——劳动对象
面向对象编程
对象的三要素
状态
行为
标识
OOP 三要素
封装性(Encapsulation):隐藏实现细节
继承性(Inheritance):接口的重用
多态性(Polymorphism):面向对象编程的设计原则、设计模式、编程技巧等等,都是围绕着多态来展开的
面向对象分析
充血模型与贫血模型
领域驱动设计 DDD
面向对象的设计
软件开发上下文:不断的需求变更
衡量设计好坏的依据
当需求不断变更时,设计是否还能满足要求?
软件的设计要用动态的眼光去评判,单纯从静态考量设计的好坏是没有意义的,要在需求不断变更的背景下审视软件设计
软件设计的臭味
僵硬:难以改变
脆弱:牵一发而动全身
不可移植:无法适应需求的变更
导致误用的陷阱:做错误的事比做正确的事更容易,引诱程序员破坏原有的设计
晦涩难懂:接手的人看不懂
过度设计
代码写出来是给人看的
设计原则
开闭原则(Open/Closed Principle,OCP)
对扩展开放,对修改关闭
不需要修改现有的实现,就可以完成功能上的扩展
实现 OCP 的关键在于抽象
依赖倒置原则(Dependency Inversion Principle,DIP)
高层模块不能依赖低层模块,而是大家都依赖一个接口
抽象不能依赖实现,而是实现依赖抽象
倒置了什么?
模块和包的依赖关系
开发的顺序和职责
精髓
首先定义一个抽象(接口),该抽象属于高层模块
高层模块不依赖低层模块,而是依赖该抽象
低层模块实现该抽象
开发顺序是高层模块先定义抽象,然后由低层模块去实现,而不是基于低层模块的实现再去定义抽象
好莱坞原则
Don't call me, I'll call you.
框架都是面向抽象编程
框架依赖抽象,而不是依赖应用程序
应用程序不应该去调用框架,而是框架调用应用程序
Tomcat 面向 Servlet 规范进行抽象,应用程序实现 Servlet 规范的接口,Tomcat 去调用应用程序的代码
里氏替换原则(LSP)
子类型 (subtype) 必须能替换掉基类型 (base type)
用来衡量继承是否合理的重要原则
里氏替换原则的关键,不是静态的分析,而是要站在程序运行的具体场景中,看子类替换父类后,程序是否能正常运行
is-a 关系是关于行为的,设计和界定一个类时,应该用行为作为区分
子类的契约不能比基类更严格
继承的缺点
继承破坏了封装,因为继承将基类更多的细节暴露给子类。因而继承被称为“白盒复用”
当基类发生改变时,可能会层层影响其下的子类
继承是静态的,无法在运行时动态扩展功能
继承可能造成类数量爆炸
组合优于继承
可能违反 LSP 的征兆
派生类中的退化函数
派生类抛出了基类不会出现的异常
单一职责原则(Single Responsibility Principle, SRP)
一个类只有一个引起它变化的原因
一个职责就是一个引起变化的原因
违反 SRP 的后果
脆弱性:当多个功能耦合在一起时,修改其中一个功能,可能会对其他功能造成影响
不可移植性:客户端不得不引入不相关的依赖
通常,当一个类比较大的时候,它的功能就不单一了,我们可以把这个大类拆分成多个单一功能的类,通过类之间的组合依赖实现相同的功能,这样代码就更易于维护和扩展了
接口分离原则(Interface Segregation Principle, ISP)
不应该强迫客户端依赖他们不需要的方法
实现手段:接口拆分,多接口实现,不同客户端依赖不同的接口
敏捷软件开发
敏捷软件开发的关键,不在于敏捷的软件过程保证,而在于设计本身是敏捷的,可以支持需求的快速迭代
反应式编程框架 Flower
传统 Web 应用的问题:大量阻塞点,耗尽线程资源,从用户的视角看服务不可用
异步
多路复用
Flower 运行环境,提供自己的线程池
异步数据库驱动
占用的线程少,就可以处理更多的客户端请求
线程不阻塞,就可以消耗更少的资源,提升吞吐量,提升响应时间
评论