面向对象编程与面向对象分析 面向对象编程不是使用面向对象的编程语言进行编程,而是利用多态特性进行编程。 面向对象分析是将客观世界,即编程的业务领域进行对象分析。 充血模型与贫血模型 领域驱动设计DDD 面向对象设计的目的和原则 软件设计的最
面向对象编程与面向对象分析
面向对象编程不是使用面向对象的编程语言进行编程,而是利用多态特性进行编程。
面向对象分析是将客观世界,即编程的业务领域进行对象分析。
- 充血模型与贫血模型
- 领域驱动设计DDD
- 易扩展-易于增加新的功能
- 更强壮-不容易被粗心的程序员破坏
- 可移植-能够在多样的环境下运行
- 更简单-容易理解、容易维护
- 为了达到上述设计目标,有人总结了多种指导原则
- 「原则」是独立于编程语言的,甚至也可以用于非面向对象的编程语言中。
- 创建模式
- 行为模式
- 结构模式
- 如同框架结构的大厦的框架
- 简化应用开发者的工作
- 实现了多种设计模式,使应用开发者不需要花太大的力气,就能设计出结构良好的程序来
- 微软公司为 Windows 编程开发了 MFC 框架。
- Java 为它的 GUI 开发了 AWT 框架
- 还有许多开源的框架:MyBatis、Spring 等
- Web 服务器也是框架:Tomcat
- 僵硬-不易改变
- 【僵化性】如果单一的改动会导致依赖关系的模块中的连锁改动,那么设计就是僵化的,必须要改动的模块越多,设计就越僵化。
- 脆弱-只想改A,结果 B 被意外破坏
- 【脆弱性】如果出现新问题的地方与改动的地方没有概念上的关联。要修正这些问题又会引出更多的问题,从而使开发团队就像一只不停追逐自己尾巴的狗一样。
- 不可移植-不能适应环境的变化
- 【牢固性】设计中包含了对其他系统有用的部分,而把这些部分从系统中分离出来所需要的努力和风险是巨大的。
- 导致误用的陷阱-做错误的事比做正确的事更容易,引诱程序员破坏原有的设计
- 【粘滞性】可以保持系统设计的方法比破坏设计的方法更难应用时。
- 晦涩-代码难以理解
- 【晦涩性】没有很好的表现出意图
- 过度设计、copy-paste 代码
- 【不必要的复杂与重复】
- 对于扩展是开放的
- 对于更改是封闭的
- 不需要修改软件实体(类、模块、函数等),就应该能实现功能的扩展。
- 关键是抽象
- 高层模块不能依赖低层模块,而是大家都依赖抽象;
- 抽象不能依赖实现,而是依赖抽象。
- 模块或包的依赖关系
- 开发顺序和职责
- 高层决定低层
- 高层被重用
- Don’t call me,I’ll call you.
- 一个正确的继承要符合什么要求呢?
- 答案:Liskov 替换原则
- 若对每个类型 T1 的对象 o1,都存在一个类型 T2 的对象 o2,使得在所有针对 T2 编写的程序 P 中,用 o1 替换 o2后,程序 P 的行为功能不变,则 T1 是 T2的子类型。
- 简言之:子类型(subtype)必须能够替换掉它们的基类型(base type)
- 形状:圆,方形
- JDK:hashtable-Properties;Vector-Stack
- 计算面积时,正方形做长方形的子类
- LSP 要求,凡是使用基类的地方,一定也适用于其子类。
- Java 语法上看,意味着:
- 子类一定得拥有基类的整个接口。
- 子类的访问控制不能比基类更严格。
- 子类的契约不能比基类更严格
- 例如,正方形长度相等,这个契约比长方形要严格,因此正方形不是长方形的子类
- 例如,Properties 的契约比 Hashtbale 更严格。
- 提取共性到基类;
- 改成组合
- 比较容易,因为基类的大部分功能都可以通过继承直接进入子类。
- 破坏封装,将基类细节暴露给子类,继承是白盒复用
- 基类改变,影响子类
- 继承是静态的,无法在运行时改变组合
- 类数量爆炸
- 也叫内聚性原则(Cohesion)
- 一个模块的组成元素之间的功能相关性
- 将它与引起一个模块改变的作用力相连,就形成了如下描述
- 一个类,只能有一个引起它的变化的原因
- 单纯谈论,定义不同
- 定义:一个职责是一个变化的原因。
- 脆弱性-把绘图和计算功能耦合在一起,当修改其中一个时,另一个功能可能会意外受损
- 不可移植性-计算几何应用只需要使用「计算面积」的功能,却不得不包含 GUI 的依赖。
- 不应该强迫客户程序依赖它们不需要的方法
- ISP 和 SRP 相关,都和内聚性有关
- SRP 指出应该如何设计一个类–只能有一种原因才能促使类发生改变。
- ISP 指出应该如何设计一个接口–从客户需求出发,强调不要让客户看到他们不需要的方法。
##
##
面向对象设计的目的和原则
软件设计的最终目的,是使软件达到「强内聚、松耦合」,从而使软件:
面向对象设计的原则
设计模式
设计模式是用于解决某一种问题的通用的解决方案。
设计模式也是语言中立的。
设计模式贯彻了设计原则。
框架(frameworks)
框架是用来实现某一类应用的结构性的程序,是对某一类架构方案可复用的设计与实现
不同领域的框架
框架 VS 工具
框架调用应用程序代码
应用程序代码调用工具
架构师框架保证架构的落地
架构师用工具提高开发效率
软件设计的「臭味」
不好的软件,会发出如下臭味
OOD 原则一:开闭原则(OCP)
OCP-OPEN/Closed Principle
传统的扩展模块的方式就是修改模块的源代码。如何实现不修改而扩展呢?
Phone-Dialer-digitButtons-sendButton【观察者模式】
OOD 原则二:依赖倒置原则(DIP)
DIP – Dependency Inversion Principle
DIP 倒置了什么?
软件的层次化
框架的核心
好莱坞规则:
程序不要调用框架,框架来调用应用程序。【依赖倒置:你想用框架,但不用去调用框架(spring,Junit,Tomcat)】
OOD 原则三:Liskov 替换原则(LSP)
在 Java/C++ 这样的静态类型语言中,实现 OCP 的关键在于抽象,而抽象的威力在于多态和继承。
1988 年 Liskov 描述这个原则:
错误示范
【使用基类的地方,不能用子类代替】
从契约的角度看 LSP
如何重构代码,以解决 LSP 问题
继承 VS 组合
继承和组合是 OOP 的两种扩展手段。
继承的优点:
继承的缺点:
应该优先使用组合
OOD 原则四:单一职责原则(SRP)
SRP – Single Responsibility Principle
什么是职责?
违反 SRP 原则的后果
后果:
OOD 原则五:接口分离原则(ISP)
ISP- interface Segregation Principle
ISP 和 SRP 的关系:
推荐书:敏捷软件开发
动手验证
# 看进程各线程的内存 / CPU 占用
top -H -p <pid>
# 某个端口被谁占着
lsof -i :8080
# 抓一次 HTTP 请求内容
sudo tcpdump -A -i any 'tcp port 8080' -c 50
延伸阅读:官方与权威资料
相关阅读(站内)
常见问题
Q:G1 GC 和 CMS 垃圾回收器有什么区别?
G1 把堆拆成多个 Region,用标记-整理避免内存碎片,目标是可预测的停顿时间;CMS 用标记-清除,回收快但会产生碎片。JDK 9 起 G1 成为默认收集器,JDK 14 移除 CMS。
Q:G1 的 Remembered Set 是干什么的?
Region 化之后跨 Region 的引用都要记账,Remembered Set 记录其他 Region 指向本 Region 的指针,回收某个 Region 时不用扫全堆,这是 G1 停顿可控的关键之一。
Q:怎么调优 G1 的停顿时间?
三步走:先把 -XX:MaxGCPauseMillis 设在合理区间(别贪低,太小会疯狂扩容堆)、再看 GC 日志里的 Mixed GC 频率定位大对象、用 -XX:InitiatingHeapOccupancyPercent 控制并发标记起手时机。
Q:为什么 Java 的 String 要设计成不可变?
不可变才能安全共享与缓存哈希值,也能安全地放进字符串常量池;一旦可变,其他线程持有引用时修改会破坏哈希表与缓存的一致性。
Q:Java 里 == 和 equals 的区别是什么?
== 比较引用地址,equals 比较对象内容。String、Integer 等包装类重写了 equals,自定义对象不重写 equals 时,equals 等价于 ==。
本文首发于 java4u.cn,转载请注明出处。
关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。

