Skip to content
难度基础(★)
建议时长45分钟

10.3 面向对象设计

面向对象设计 OOD 把分析模型进一步转化为可实现、可维护的软件设计。软考中这一部分主要考设计原则,尤其是七大原则的名称与描述匹配。

从结构化原则到面向对象原则

结构化设计强调“高内聚、低耦合”。面向对象设计也继承这个方向,只是对象世界中关注点变成类、接口、继承、组合和包。

结构化表达面向对象中的对应思路
高内聚单一职责,类或接口目的单一
低耦合接口隔离、组合复用、迪米特法则
减少修改影响开闭原则、依赖倒置、里氏替换

OOD 的原则不是互相完全孤立的,它们都在服务同一目标:降低修改成本,提高复用性、扩展性和可维护性。

七大设计原则

原则一句话题干关键词
单一职责原则 SRP一个类最好只有一个引起它变化的原因目的单一、职责单一
开闭原则 OCP对扩展开放,对修改封闭新增功能靠扩展,少改旧代码
里氏替换原则 LSP子类可以替换父类出现的位置子类替换父类、特殊替换一般
依赖倒置原则 DIP依赖抽象,不依赖具体实现针对接口编程、高层抽象
接口隔离原则 ISP使用多个专门接口,避免大而全接口接口目的单一、不要总接口
组合复用原则 CRP优先用组合实现复用,而不是继承组合优于继承、降低耦合
迪米特法则 LoD一个对象应尽量少了解其他对象最少知识法则、只和必要对象交互

这些原则中,题目常给一句定义,让你选原则名称。

开闭原则:为什么“扩展开放、修改封闭”

修改已有代码可能引入新缺陷,也会触发回归测试。开闭原则希望面对变化时,尽量通过新增类、新增实现、新增配置来扩展,而不是直接改动稳定代码。

做法后果
每次新功能都修改旧类影响范围大,回归成本高
把变化点抽象出来,通过新增子类或策略扩展旧代码更稳定,扩展更安全

开闭原则不是“不准修改”,而是设计时尽量让常见变化通过扩展完成。

里氏替换:子类要守父类契约

里氏替换原则强调:凡是能使用父类对象的地方,都应能使用子类对象而不破坏程序正确性。

如果子类继承父类后大幅改变父类行为,使调用方原有假设失效,就违反了里氏替换。它隐含的建议是:子类扩展父类能力,但不要破坏父类已有语义。

依赖倒置:针对抽象编程

传统调用常表现为高层模块依赖低层具体实现。依赖倒置要求高层模块不要直接依赖低层具体类,而要依赖抽象接口;低层实现也围绕这个抽象来实现。

mermaid
flowchart LR
  A["高层业务"] --> I["抽象接口"]
  B["具体实现A"] --> I
  C["具体实现B"] --> I

这样新增具体实现时,高层业务不需要知道每个子类细节,多态和扩展都会更自然。

接口隔离与组合复用

接口隔离原则反对把所有功能塞进一个大接口。大接口会让使用者被迫依赖自己不需要的能力,一处变化影响很多调用方。更好的方式是按职责拆成多个小接口。

组合复用原则强调优先通过对象组合来获得能力。继承关系耦合紧、层次一旦设计错就很难改;组合关系更灵活,可以在运行时替换组件,修改影响也更小。

复用方式优点局限
继承复用简单直接,可复用父类实现耦合紧,父类变化影响子类
组合复用灵活、低耦合、可替换需要设计清晰接口

这也是很多设计模式更偏向“组合优于继承”的原因。

迪米特法则

迪米特法则又叫最少知识法则。对象只应了解自己必须了解的对象,不要到处访问别人的内部细节。

如果一个类知道太多其他类的结构,任何被它了解的类发生变化,都可能牵连它修改。迪米特法则通过减少知识面来降低耦合。

包相关原则

字幕还补充了几条包层面的原则,考试频率较低,但要能对上名称。

原则含义
重用发布等价原则重用的粒度应与发布的粒度一致
共同封闭原则一个包中的类应对同一类变化共同封闭
共同重用原则一个包中的类应共同被重用
无环依赖原则包之间依赖关系不要形成环
稳定依赖原则依赖应指向更稳定的包
稳定抽象原则包的抽象程度应与稳定程度匹配

这些原则主要服务包结构设计,不是本节最高频,但遇到名称配对题时要有印象。

例题

单选
进行面向对象设计时,若要求一个类仅有一个引起它变化的原因,属于:
单选
开闭原则强调:

本节小结

面向对象设计的考试重点是原则匹配。单一职责看“一个变化原因”,开闭原则看“扩展开放、修改封闭”,里氏替换看“子类替换父类”,依赖倒置看“依赖抽象”,接口隔离看“小而专门的接口”,组合复用看“组合优于继承”,迪米特看“最少知识”。