资讯动态

软件工程中的面向对象设计方法:从分析模型到设计模型

发布时间:2026/10/2 10:10:50 来源:尧图企业网站定制
复习到软件工程第七章“面向对象的设计方法”时我最大的感受是这一章跟前面几章不一样前面很多内容是“概念性”的背一背能混过去而这一章是真正决定你会不会做设计的。面向对象这个词大家都很熟但怎么把“对象”“类”“消息”这些概念用在一套完整的设计里很多人其实是含糊的。这一章恰好把这个问题讲清楚了它告诉你从分析阶段拿到的需求怎么一步步变成可以落地的类、接口、模块、子系统甚至怎么判断一个设计是优是劣。如果你是正在备考的软件工程学生或者准备做软件工程课程设计、毕业设计又或者刚开始用 Python、Java、C# 写面向对象代码但总觉得“类分得不对味”这一章都值得认真过一遍。1. 面向对象设计在软件工程里的位置它到底解决什么问题1.1 从结构化设计到面向对象设计思路的一次转向很多教材都会把“结构化设计”和“面向对象设计”放在一起对比这不是没有原因的。早期的软件规模小用功能分解的思路把一个系统拆成一个个子功能模块每个模块有自己的输入输出数据在模块之间传来传去。这种设计在需求稳定的时候效率很高但一旦需求变了数据格式也得跟着变接口要改调用链也要改最后经常是这个模块改了引发那个模块跟着崩改动范围像滚雪球一样越来越大。面向对象设计换了一个基本思路不再围绕“功能”拆而是围绕“对象”拆。一个对象把数据和操作它自己的行为放在一起外面只能通过它暴露的接口跟它打交道。它的核心价值是挡住变化。举个例子你做了一个用户模块登录、注册、改密码都在用户对象里如果数据库从 MySQL 换成别的你只需要改用户对象内部的数据访问部分调用方根本不用动。这就是面向对象设计在工程上的真正意义它不是让你把代码写得“看起来更高级”而是为了应对需求变化时减少连锁破坏。我复习这一章的时候印象最深的类比是做菜。结构化方法像一条流水线一个工序切菜一个工序煮菜一个工序摆盘数据流在各道工序之间传面向对象方法则像一个餐厅厨房每个厨工带齐自己的工具和食材洗、切、炒、摆都封装在一个个独立岗位上菜单变了只需要调整对应岗位的做法不需要把整条流水线拆了重装。这个类比虽然简单但用来理解“为什么转向面向对象”特别好用。1.2 面向对象分析与面向对象设计的分工书上会把整个生命周期分成分析、设计、实现几个阶段其中最容易混的就是 OOA面向对象分析和 OOD面向对象设计。我一开始也记不住后来用一句话就分清了分析问“系统应该做什么”设计问“系统怎么做才做好”。更具体地说OOA 阶段的产出是用例图、领域类图、顺序图这一类模型它们从用户视角描述现实世界的概念比如借书系统里有“读者”“图书”“借阅记录”这些实体它们之间的业务规则是什么。到了 OOD 阶段要在这个基础上做工程化加工比如把“读者”类拆成“读者界面类”“读者控制类”“读者实体类”增加数据访问、权限控制、异常处理等分析阶段没有的东西。分析模型里一个概念可能对应设计模型里多个类设计模型里也可能合并或删掉一些不必要的类。复习时最容易踩的坑就是把分析模型直接当设计模型交上去。考试时如果给你一个需求让你画设计类图你只把领域模型里的实体原封不动画出来通常拿不到高分因为缺少界面类、控制类、工具类这些设计层面的考虑。判断自己有没有掌握可以找一个熟悉的系统比如图书馆管理、学生选课、网上书店先做分析建模再做设计建模两者一对比差别就很明显了。2. 设计前必须吃透的几个基础概念2.1 对象、类、消息面向对象的三块积木很多人写过程序但未必认真琢磨过这三个词的设计含义。类是一个模板描述一类事物的共同属性和行为对象是这个模板的实例是运行时真实存在的东西消息则是对象之间沟通的方式一个对象调用另一个对象的方法本质就是发送了一条消息。可以这样理解类是一张房产户型图对象是依照图纸盖出来的某一套房消息就是你和邻居之间按门铃、递东西的动作。用代码来说Python 里定义一个类、实例化对象、调用方法就是这三块积木最直接的体现class Book: def __init__(self, title): self.title title def show(self): return f《{self.title}》 book_a Book(软件工程导论) print(book_a.show())这里的 Book 是类book_a 是对象book_a.show() 就是给对象发了一条消息。设计阶段的“找类”工作本质上就是在需求里识别出有哪些可复用的模板这个能力比多背几条定义重要得多。我复习的时候把教材里“候选类”识别方法反复看了几遍核心就是找名词和名词短语再看它们有没有独立属性、有没有可行为最后筛掉冗余的、伪的、描述性的候选类。2.2 封装、继承、多态的设计含义封装大家都会说“隐藏细节、暴露接口”但设计时怎么落地才是关键。封装不是简单的 private 关键字而是一条设计纪律类对外公开的方法要尽可能少能不给外部改属性机会就不给。实际做项目时我见过太多把属性写成 public、内部状态随便改的代码表面上是“图方便”实际上出了问题根本不知道是哪个对象改坏了数据。给属性加访问控制、给修改操作加业务校验这才是封装真正的价值。继承在考试里考得很多但实际设计时要慎重。继承表达的是“is-a”关系子类继承父类的公共特征同时可以扩展和重写。问题是继承层次一旦深了底层的任何修改都可能影响上面一堆子类形成强耦合。我见过一个项目里类继承链有五六层为了复用几个方法硬造出一棵继承树最后加一个需求要动三个父类那次之后我就记住了“组合优于继承”不是一句空话。多态是面向对象设计里最容易被低估的一块。它的设计含义是调用方只要面向抽象编程不需要关心具体实现是哪一个子类。Java 里的接口、Python 里的抽象基类、C# 里的 interface都是为多态服务的。设计模式里很多套路都建立在多态之上比如策略模式把不同算法放进不同策略类调用方通过一个共同接口去调用新增算法只加类、不改旧代码。学到后面你会发现多态才是面向对象设计真正的大杀器。2.3 关联、聚合、组合对象之间的关系不能想当然对象之间不是孤立的设计时确定好它们之间的关系比给每个类写属性要难得多。教材里最容易考的就是聚合和组合的区别。聚合是整体与部分的关系但部分可以脱离整体独立存在比如班级和学生班级没了学生还在组合是强聚合部分的生命周期跟整体绑定比如订单和订单项订单删了订单项也没有独立意义。用生活里的例子更好记一个电脑包里装着鼠标、笔记本、充电线这是聚合包坏了里面的东西还在一台一体机电脑的屏幕和主板就是组合拆了机器这两个东西基本上失去原本的意义。我在实际编码中判断聚合还是组合就盯着一个点看生命周期部分和整体一起创建、一起销毁的是组合能各自独立存在的是聚合。关联和依赖也要分清楚。关联是长期稳定的结构关系比如教师和学生之间存在“执教”关系依赖是临时的使用关系比如一个类的方法里用到了另一个类作为参数这种关系强度更弱体现在 UML 上是不同的线型。考场上看图题特别爱考这几个符号我建议复习时把关联的实线、聚合的空心菱形、组合的实心菱形、依赖的虚线箭头分别画三遍符号记熟了做题才能快。3. 面向对象设计的过程从分析模型到设计模型3.1 系统设计先定架构再谈对象很多人以为面向对象设计就是不断地画类图实际上正经流程是先做系统级设计再细化到对象级。系统设计要回答几个比较大的问题系统分哪些子系统或层次客户端和服务端怎么划分哪些任务可以并发执行数据存到什么地方这些决策定了底下的类设计才有边界。分层是系统设计里最常见的手段业务系统通常会分表示层、业务逻辑层、数据访问层。这样分的目的是让每层只干自己的事界面层管交互展示业务层管规则计算数据层管数据库读写。层与层之间通过约定好的接口通信替换某一层时不至于牵连全局。做课程设计或毕业设计的时候哪怕系统不大也建议按这个思路去分层代码的整洁度和可维护性会有明显差别。并发和数据存储也是系统设计阶段要想清楚的。一个图书管理系统看起来不复杂但如果有多个借阅台同时办理业务就可能出现同一本书被两个人同时借到的情况。这类问题不解决后面类设计得再好也会出事。数据存储方面要决定哪些对象需要持久化用关系数据库还是文件怎么建表对象和表之间怎么映射。这些在分析阶段通常不深入但设计阶段必须给出方案。3.2 对象设计把类、属性、方法一项项敲定系统级方案定了以后才进入对象设计也就是把每个类的细节敲定。这里包括确定类属性的可见性是 public、protected 还是 private确定方法的签名参数有哪些、返回值是什么还要细化对象之间的关联比如一对多还是多对多能不能双向导航最后要检查每个类承担的责任是不是单一如果一个类又管界面又管计算又管数据库就要拆开。对象设计阶段有一个重要的练习就是“走查一遍消息”。挑一个核心用例把参与的对象画出来然后从发起方开始逐个模拟对象之间怎么通过消息协作完成这个功能。比如“用户借书”这个用例借书界面收到用户输入后向借书控制类发消息控制类校验读者状态、检查图书库存、创建借阅记录最后把结果返回给界面。这样走查下来所有对象的方法基本就找全了类图也能自然画出来。我自己的体会是对象设计最怕“一步到位”的心态。很多同学画类图喜欢一次画出很完美很细的样子结果一验证就发现漏了消息返回路径、漏了异常分支。比较可靠的做法是先用粗粒度类图把整体结构搭出来再按用例去走消息流逐步补充方法细节走查两三轮以后类图才真正达到可指导编码的程度。3.3 从分析模型到设计模型借书案例的映射过程用借书这个经典例子可以很清楚地看到分析模型怎么变成设计模型。分析阶段领域模型里会有读者、图书、借阅记录这几个实体用例是“借书”。设计阶段情况就不一样了界面层需要一个借书窗体类业务层需要一个借书控制类负责处理借书流程数据层需要书库类负责查询图书和保存记录原有的读者、图书实体类继续保留但属性、方法要进一步细化。我复习时自己动手画过一遍从用例图出发把分析类图先画出来然后增加界面类和控制类再画出每个类的属性和方法最后用一张顺序图验证借书流程能不能走通。这个过程最初很慢但画多了以后会形成肌肉记忆考试时遇到类似题目头脑里会自动浮现这条映射路径。很多人觉得案例分析题难其实难就难在少了这个“按流程映射”的训练。这里还要提醒一句并不是所有分析类都会保留到设计阶段也不是所有设计类都能从分析阶段直接找到来源。控制类、界面类、工具类很多时候是设计阶段新增的考试时你把它画出来恰恰是得分点说明你理解了分析和设计的区别。4. 判断设计好坏的原则一把不会过时的尺子4.1 开闭原则与单一职责原则设计模式里那些花哨方案都可以不会但 SOLID 原则里的开闭原则和单一职责原则一定要吃透这是被反复考的“题眼”。开闭原则讲的是对扩展开放、对修改关闭翻译成人话就是系统加新功能时最好只加新代码不要回头改动旧代码。现实中的例子很多比如一个报表系统新增一种导出格式不应该去改原来的导出方法而应该新增一个导出类通过策略替换上去。单一职责原则讲的是一个类只负责一件事。判断标准很简单能不能说清楚这个类“为什么会被修改”如果它有多个可能引起修改的原因比如既要管业务计算又要管页面显示还要管数据库读写那它就该拆开。这有点像让一个人又当厨师又当服务员又当收银员短期看着省人生意一忙必乱。代码写成这样改一个需求就要动好几个互不相干的地方后期维护成本极高。我实际写代码时有个习惯当一个类超过两三百行、方法超过两三个职责时就会停下来认真审视它是否违背了单一职责。这个习惯不是天生的是踩了无数次改一处崩一处的坑以后养成的。考试时如果让你重构一个类优先检查它是不是承担了过多职责往往能快速找到突破点。4.2 依赖倒置与接口隔离依赖倒置原则是很多人觉得绕的一条它说高层模块不应该依赖低层模块二者都应该依赖抽象。这句话用生活类比就很容易懂电脑不应该直接焊死某个鼠标而是提供 USB 接口任何符合 USB 标准的鼠标都能插上去用。高层模块相当于电脑低层模块相当于具体鼠标接口就是 USB 标准。设计系统时业务层依赖一个“数据库访问接口”而不是直接依赖 MySQL 的某个具体类以后换数据库就不用动业务逻辑。接口隔离原则讲的是不能强迫调用方依赖它用不到的方法。比如你设计一个“动物”接口里面有“飞”的方法结果企鹅类也得实现“飞”但只能抛异常这就不合理。正确的做法是把大接口拆成小接口让每个类只实现它真正需要的功能。我现在设计接口时习惯问一个问题这个接口的每个方法是否真的会被所有实现类用到只要有一个实现类用不到就要考虑拆接口。这两条原则在面向对象编程里面特别实用尤其是做 Java、C# 这类强类型语言项目时接口设计得好不好直接决定后面扩展顺不顺畅。Python 虽然是动态语言不强制定义接口但用抽象基类来落实依赖倒置同样常见复习时尽量把原则和语言结合起来记比空背定义记得牢。4.3 里氏替换与组合复用里氏替换原则说的是子类应该能替换父类并保持程序行为正确。最经典的坑是“正方形继承矩形”。矩形有宽和高正方形要求宽高相等如果让正方形继承矩形重写设置宽高的方法去强制两边相等那么依赖矩形的一组代码在替换成正方形后行为就变得不可预测。这提醒我们继承不是看概念上像不像而是看行为上能不能替换。组合复用原则则是解决继承弊端的常用方案优先使用组合或聚合而不是继承来复用功能。因为继承是静态绑定的运行期改变不了而组合通过持有其他类的对象可以在运行期灵活替换行为。举个例子一个汽车类可以继承“汽油引擎”得到一个汽油车但只要想换电动车就得重新设计如果用组合汽车类里持有一个引擎对象根据配置注入汽油引擎、电动引擎或混动引擎扩展起来好得多。复习到这里你就会发现面向对象设计原则之间是相互支撑的。开闭原则是目标依赖倒置是实现手段里氏替换保证多态不出错组合复用降低继承带来的耦合。考试出案例分析时往往不是单考一条原则而是一道题里融合好几条要能根据代码情境说出违反了哪条、怎么改。5. 把设计画明白UML 与设计表达5.1 类图设计模型的静态骨架类图可以说是我复习这一章时投入时间最多的一块。类图的三段式结构看起来简单上面是类名中间是属性下面是方法但真正难的是类之间的关系。关联、聚合、组合、依赖、泛化、实现这几种关系各有各的符号画错一个就容易被扣分所以一定要用题目反复练。画类图时要注意可见性符号public 用加号、private 用减号、protected 用井号。属性名和方法名旁边要标注数据类型方法还要标参数和返回类型。类与类之间的多重性也要标清楚比如一个读者可以借多本图书那读者和图书之间就是一对多要在关联线两端标注“1”和“0..*”。这些细节单独看不难但合在一起很容易在考试时漏掉。我复习的时候练过一个网上书店的类图书籍、订单、订单项、会员、购物车。最开始画得一塌糊涂订单项和订单之间画成了聚合后来意识到购物车和购物车项是同生共死的应该用组合会员和订单是整体与部分但订单可以独立存在吗显然不能所以是聚合还是组合要看具体业务。这类练习做多了画图自然就有手感了。5.2 顺序图用消息串起整个交互过程类图是静态的顺序图是动态的它关心的是对象之间在某个场景下如何发消息。顺序图里每个对象底部有一条垂直的生命线对象之间用横向箭头表示消息箭头带上调用方法名和参数。比如用户借书借书窗体先发送“录入借书信息”给借书控制器控制器再向书库发“查询库存”书库返回结果控制器再向借阅记录发“创建记录”最后窗体显示“借书成功”。顺序图的价值在于验证设计是否可行。类图画得再漂亮把消息一条条捋下来往往会发现某个类缺少对应方法、某个消息返回方向不对、或者某些交互步骤重复了。所以实际做设计时类图和顺序图是配合使用的类图给顺序图提供对象来源顺序图反过来修正类图的方法列表。复习时很多人只画类图不画顺序图这是一个很大的误区。如果时间有限我反而建议先挑两到三个核心用例画顺序图再去补类图这种方式更接近真实设计过程中的思考路径。5.3 状态图与活动图不同场景用不同的图除了类图和顺序图面向对象设计中还会用到状态图和活动图。状态图描述单个对象在其生命周期里状态的变化比如订单的状态有“待支付”“已支付”“已发货”“已完成”触发条件分别是“支付成功”“卖家发货”“买家确认收货”。设计状态图时要注意状态最好有限且可枚举状态转移的触发条件要写清楚否则实现时容易漏分支。活动图更像传统流程图适合表达一个相对复杂的业务流程比如“借书流程”里先验证读者身份、再检查库存、再更新记录、最后通知读者。活动图在描述并行流程时特别有用可以画出分叉和汇合。在软件工程课程设计里活动图可以被用来画系统的业务流程图评阅老师看了能快速理解你的业务逻辑。我自己的经验是不要试图用一张图表达所有信息。一个复杂系统可能需要好几张不同视角的图类图负责结构、顺序图负责典型交互、状态图负责关键对象的状态、活动图负责业务流程。每张图只讲清楚一到两件事设计表达才清晰。考试时给到你的往往是具体语境你要根据题目要求选对合适的图而不是把会的图全画一遍。5.4 画图时最容易忽视的四个细节画 UML 图有几个细节极容易被忽视但往往就是扣分点。第一多重性漏标。类图里“1”和“0..*”不标等于关系没说清楚。第二箭头方向画反。依赖关系是虚线箭头指向被依赖者泛化关系是空心三角指向父类方向反了整个图的意思就变了。第三把聚合和组合符号搞混空心菱形和实心菱形区别很大画错至少丢两三分。第四顺序图里消息顺序号和生命线激活条没有体现导致交互流程看起来混乱。还有一个容易被低估的细节是图面组织。实际做项目评设计文档时图不是画出来就完事还要考虑阅读顺序。类图按照子系统或模块分组顺序图按照用例组织图与图之间保持一致的类名和方法名。命名不一致是最让人头疼的问题同一张设计图里“Book”和“books”混用老师看的时候印象分会大打折扣。6. 高频考点与实践避坑速查6.1 概念辨析题这几组说法必背这一章的概念辨析题出题面很广但翻来覆去其实就集中在几组对比上。面向对象分析和面向对象设计的区别是最常考的分析面向现实世界、设计面向软件实现分析关注功能、设计关注质量与约束。聚合和组合的区别也是必考核心就看是否同生命周期。继承和多态的区别也常考继承是类之间的一种结构关系多态是运行时的行为替换能力。我复习时做过一张速查表把容易混淆的几组概念放在一起对比对比项核心区别记法分析 vs 设计做什么 vs 怎么做先分析后设计聚合 vs 组合部分能否独立存在同生共死是组合继承 vs 组合静态复用 vs 动态复用组合更灵活关联 vs 依赖长期结构 vs 临时使用依赖更弱封装 vs 多态隐藏实现 vs 替换行为一个对内一个对外这类对比不需要死记关键是把每个概念背后的设计意图说出来。答题时如果能讲一句“用组合替代继承的动机是降低耦合”比单纯罗列定义更能拿分。6.2 画图题和案例题的操作步骤考试遇到给出需求让你画设计类图或顺序图的题我建议按固定步骤走不要上来就画。第一步细读需求圈出名词找候选类圈出动词找方法线索。第二步先画分析模型用领域类明确实体和关系。第三步做设计加工增加界面类、控制类、数据访问类确定每个类的属性和方法。第四步画顺序图验证一个核心场景比如新增订单、完成支付等。第五步再回头调整类图补充缺失的方法和关系。这里要特别注意时间分配。画图题很容易在细节上耗太多时间导致后面的题来不及做。我一般会给自己定一个原则先保证图的主要结构完整再管可见性符号和多重性标注。如果时间不够宁可少标一个符号也要把类和关系画全。做题时还有一个技巧是画完图以后用文字补充设计说明比如“这里采用组合关系因为订单项与订单生命周期一致”“这个类遵循单一职责原则负责借阅业务规则的校验”。这种说明可以向阅卷者展示你的设计思路往往能弥补图上的小瑕疵。6.3 复习时最容易踩的三个坑第一个坑是只背概念不画图。面向对象设计是实践性很强的知识不亲手画几遍类图和顺序图永远发现不了自己哪里没懂。第二个坑是只写代码不做设计。有的同学代码写得挺熟但让他解释类图里的聚合关系他却说不清楚这说明他把设计和实现割裂了。第三个坑是忽视了设计原则的运用。考试案例分析往往要求指出代码问题并给出修改方案这需要你真正理解开闭、依赖倒置这些原则而不仅仅是背名字。根据我复习的经验最有效的复盘方式是把借书系统从头到尾走一遍完整流程画用例图、画领域模型、画设计类图、画顺序图、标出耦合低的地方和改进点。这个过程不仅覆盖了本章大部分考点也基本覆盖了软件工程课程设计和毕业设计里需要展示的分析设计能力。真正做完一遍后再回头做题会明显感觉题目里那些“考点”清晰了不少。最后再分享一个小技巧遇到设计相关的简答题可以先从“这个设计要解决什么问题”入手展开再谈具体手段。比如问你对“面向对象设计方法的理解”可以先说它应对的核心问题是需求变化再说对象封装、继承、多态这些手段最后说原则和质量保障。这种答题逻辑和设计逻辑是一致的阅卷老师看着也舒服。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑