面向对象分析与设计(OOAD)入门
一、对象、类与封装
对象(Object)是「具有状态和行为的一个实体」——状态即数据(属性),行为即能做的操作(方法)。类(Class)是对一类对象的抽象描述:这类对象有哪些属性、有哪些方法。对象是类的实例:根据类「造」出来的具体个体。例如「订单」是类,某笔订单号 10086 的订单就是对象。
封装(Encapsulation)是把数据与操作绑在一起,并隐藏内部实现、只暴露必要的对外接口。例如订单的「总价」:内部可能按 items 遍历计算,但对外只提供 getTotal();调用方不关心你怎么算、用没用缓存,只要结果。这样内部实现可以改(例如加优惠券逻辑),只要接口不变,调用方不受影响。用访问控制(public/private 等)把「该藏的」藏起来,就是封装在语言层面的体现。
二、职责划分与单一职责
一个类(或模块)最好只为一类变化原因负责——这就是单一职责原则(SRP)。例如「订单」负责订单状态与订单内的计算;「支付」负责调用支付渠道、处理支付结果;「通知」负责发邮件/短信。若把订单、支付、通知全塞进一个类,以后改支付逻辑要动订单、改通知方式也要动订单,职责混杂、难以维护。
分析需求时,可以问:「这里谁该对什么负责?」把「谁」落实成类或模块,把「什么」落实成该类的职责;若一个类要负责多件不相关的事,就拆成多个类,通过组合或接口协作。
三、继承、组合与多态
继承(Inheritance)是「子类拥有父类的属性和方法,并可扩展或覆盖」。例如「支付」有「支付宝支付」「微信支付」两个子类,都具备「发起支付、查结果」的通用行为,各自实现具体调用逻辑。组合(Composition)是「类 A 里持有类 B 的实例,通过 B 做事」——A 有 B,而不是 A 是 B 的一种。例如订单里「有一个地址」「有一个支付单」,用组合更自然。
- 「支付宝支付」是一种「支付」
- 子类继承父类属性与方法,可覆盖或扩展
- 适合:真正的分类关系、可替换的多种实现
- 「订单」有一个「地址」、有一个「支付单」
- 通过持有其它对象协作,不继承
- 适合:部分与整体、依赖多种能力
设计时优先考虑组合:组合更灵活、不绑死继承链,符合「针对接口编程」;继承层次不宜过深,否则理解与修改成本高。多态(Polymorphism)是「同一接口、不同实现;调用方只依赖接口,运行时可替换成不同实现」。例如「支付」接口,运行时注入支付宝或微信实现,调用方代码一样,行为不同。多态常通过继承或接口实现,是「面向接口编程」的体现。
四、从需求到领域模型的第一步
领域模型是用对象和类把「业务里有哪些概念、它们之间什么关系」表达出来,不涉及界面、不涉及具体技术实现,只关心业务本质。从需求到领域模型的第一步,就是:从需求中识别名词与动词——名词往往是候选的类(或属性),动词往往是候选的职责或方法。
具体可以:从用例或用户故事里摘名词(订单、支付、用户、地址)→ 哪些是「东西」、哪些只是别的属性;摘动词(下单、支付、取消)→ 谁该负责这个动作。然后给每个「东西」定一个类,把属性和方法分配上去;类与类之间是继承还是组合、谁依赖谁,画成简单的类图或列表,就是领域模型的雏形。后续设计(分层、接口)会在这个模型上展开。
一句话: 面向对象用对象(状态+行为)和类(一类对象的描述)建模;封装隐藏内部、只露接口;单一职责让一个类只为一类变化负责;继承表达「是一种」,组合表达「有一个」,优先组合;多态让同一接口有多种实现。从需求到领域模型,先识别名词与动词,再落实为类与职责、类与关系。
五、小结
对象有状态与行为,类是同类对象的抽象;封装把内部藏起来、只暴露必要接口。单一职责要求一个类只为一类变化原因负责。继承表达 is-a、组合表达 has-a,设计时优先组合;多态是同一接口多种实现、面向接口编程。从需求到领域模型:从需求中识别名词与动词,得到候选类与职责,再整理成类与关系,作为设计与代码的基础。下一章讲UML 基础:用例图、类图、序列图,把分析与设计画出来。