架构师训练营第 1 期第 2 周学习总结
第2周 框架设计
1. 面向对象编程
编程语言的实质
1 在计算机所能理解的模型和现实世界之间建立一种联系。
2 将现实世界中的领域问题,分析抽象,生成一个模型,然后通过编程语言将这个模型实现。
3 模型一方面客观地反映了领域问题,跟客观世界的实体一一对应;另一方面,同软件实现中的对象也是一一对应的。
4 合理的抽取的模型,从而真实的反映客观需求,同时也反映如何开发系统。软件编程就变成用面向对象语言将抽象出来的模型模型实现的过程。
编程的核心要素
1 机器
汇编语言针对机器编程
2 人
面向过程语言针对人编程
3 业务领域
面向对象语言针对客观业务领域编程,面向数据编程本质上是面向对象编程的一个分支
把握了编程语言的核心要素,也就把握了编程语言的发展脉搏,未来出现新的语言,就可以准确判断新语言更细分了,还是倒退了。
面向对象编程要素
封装性 继承性 多态性
这些要素都不是面向对象编程语言独有的。
面向对象的各种编程技巧、设计模式、设计原则,都是围绕多态展开。掌握好多态,才能掌握好面向对象编程。面向对象编程,不是用面向对象语言进行编程,而是利用多态特性进行编程。
面向对象分析
面向对象分析是针对客观世界业务领域问题进行对象分析,指导对象设计。
贫血模型充血模型
领域驱动设计
面向对象设计目标
目标:高内聚、低耦合,使系统
易扩展--容易增加新功能
更强壮--不易被粗心程序员破坏
可移植--能够在多环境运行
更简单--易于理解、维护
面向对象的设计方法
如何实现高内聚,低耦合:
1 遵循设计原则
2 利用设计模式:遵循设计原则的模式
3 利用框架:基于设计原则设计模式开发的结构性程序
框架
1 实现某一类应用的结构性程序,对某一类架构方案的可复用设计实现。
2 简化开发者工作。
3 实现多种设计模式,使开发者不用很多精力就能设计出良好结构的程序。
4 框架 vs 工具:框架调用应用程序代码,应用程序代码调用工具
5 架构使用框架保证架构落地,用工具提升开发效率。
2. 代码的坏味道
僵化性:
很难对代码改动。单一改动会导致对依赖模块的连锁改动,要改动的模块越多,系统越僵化。
脆弱性:
对系统改动会导致与改动无关的其他地方出现问题。switch/case, if/else比抽象接口类更脆弱。
牢固性:
将部分代码从系统中分离成开重用组件,面临巨大风险。
粘滞性:
由于先前的错误改动方式,使得保持系统设计比破坏系统设计更难,做正确的事情比做错误的事情更困难。
复杂性:
设计中包含没有用的组成部分。当开发处理预测潜在变化,并在代码中放置处理潜在变化的代码,就会发生这种情况。
重复性:
设计中包含重复结构,而重复结构本身可通过单一抽象统一。编程时使用拷贝粘贴时,要留意这种情况发生。
晦涩性:
代码难以阅读、理解,随着时间推移,代码晦涩性会越来越强。
3. SOLID原则
从面向对象设计目标层面分类:
· OCP DIP LSP 更多关注低耦合
· SRP ISP 更多关注高内聚
·从面向对象特性层面分类:
· OCP、DIP针对多态,通过多态实现原则。
· LSP针对继承,限制继承的使用。
· SRP、ISP针对封装,指导类、接口本身的封装性设计。
开放封闭 OCP
对扩展开放,对修改封闭。不需要修改已存在的软件实体,就能实现功能的扩展。
如何实现:关键是抽象。定义抽象接口,使得接口本身和接口的调用保持不变,添加新功能时,对抽象接口进行扩展,从而实现开闭原则。
依赖倒置 DIP
高层不依赖于底层模块,而是大家都依赖于抽象(接口)。
抽象(接口)不依赖于实现,而是实现依赖于抽象。
低层模块实现一个接口,供高层模块调用不是依赖倒置;而高层模块定义一个接口,供低层模块实现,才是依赖倒置。这种倒置也倒置了开发者的依赖关系。
高层不依赖于低层模块,而是依赖于高层接口,使得高层更容易被复用。
DIP倒置了:
o 模块/包的依赖关系
o 开发顺序和职责
软件层次化:
o 高层决定低层
o 高层容易被复用
框架的核心:
o 框架不依赖于应用程序,应用程序依赖于框架(实现框架定义的接口)
o 好莱坞规则:Don't call me, I will call you.
o 倒转的层次依赖关系
里氏替换LSP
子类型必须能替换他的父类型,能不能替换,要放在场景中观察。
不符合IS-A关系的继承,一定不符合LSP
IS-A关系是关于行为的,设计界定一个类,应以其行为作为区分。
LSP要求,凡使用基类的地方,一定能使用其子类,因此:
o 子类一定拥有基类的整个接口。
o 子类的访问控制不能比基类更严格。
o 子类的契约不能比积累更严格。
如何重构代码解决LSP问题:
1. 提取共性到基类
2. 用组合替代继承
继承优缺点:
o 继承比较容易,大部分基类功能可通过继承进入子类。
o 继承破坏了封装,将基类功能暴漏给子类。
o 当基类发生变化,可能层层影响下面的子类
o 继承是静态的,无法运行时改变组合。
o 继承可能导致类数量爆炸。
优先使用组合替代继承。
检测LSP:
一个继承关系,如果孤立地看,不能判断继承意义的有效性,放在程序运行的调用上下文才能看出是否有问题。
可能违反LSP的征兆:
o 从基类到派生类出现退化函数。
o 派生类抛出异常,而基类本身不会抛出异常。
单一职责 SRP
一个类只能有一个因其他变化的原因,一个职责是指一个变化的原因。
违反SRP的后果:
脆弱性-- 将多个职责放在一起,修改其中一个,可能让另外一个意外受损。
不可移植性-- 外部应用本应只依赖一个功能,却被迫包含多个功能依赖。
在业务开发过程中,倾向于写一个大类,所有的方法都放在其中,通常当一个类太大时,类的职责就不单一了。当查看一个类需要频繁滚动屏幕,类可能就不单一了。
可以把一部分功能拆解分离到另一个类中,通过类的组合实现复杂功能。
有时区分一个类包含几个职责并不明显。
何时分离职责?当发生变化时。
常见的违反SRP原则的情形:
类中包含的两个职责,变化的原因不同,变化的快慢也不同。
接口隔离 ISP
不应该强迫用户依赖他们不需要的方法。
ISP与SRP关系:
o 都和'内聚性'有关。
o SRP关注类的内聚性,如何设计一个类。
o ISP关注接口的内聚性,从客户的角度出发,如何设计一个接口。
o IPS可作为SRP的补充,当类本身已足够内聚无法继续拆解,通过多继承分离多个接口,使得类使用者只依赖他所需要的类接口。
·
评论