浅谈 OOD
定位
结合个人学习经历,简单总结一下个人对于面向对象设计的认识
前言
面向对象作为一种重要的编程思想,为编程提供了一套有效的抽象框架、对编程过程起到了极大的指导作用。
个人与面向对象的“孽缘”源于大一春季学期的高级语言程序设计II (C++),当时初步接触了面向对象的基本思想并利用 C++ 进行了简单实践;在大一夏季学期的程序设计实践课程设计中利用 C++ 照猫画虎地实践了面向对象编程;在大二秋季学期的面向对象建模技术课程中对面向对象设计原则和具体执行过程有了进一步的认识;在同一学期的 Java 设计课程中利用 Java 较为熟练地对面向对象编程进行了进一步的实践;在大二夏季学期的软件工程课程设计中简单练习了面向对象设计,并熟练运用面向对象思想进行 Web 开发;在大三秋季学期的虚拟现实开发技术课程中进一步学习和实践了面向对象设计。可以说自从接触了面向对象,后续课程就不断使用这种思想进行开发,现在综合上述的学习和实践经历,总结个人对面向对象设计(下统称 OOD)的理解
OOD 原则回顾
SOLID
原教旨主义 SOILD 只有以下五项
- Single Responsibility Principle:单一职责原则
一个类应该只有一个发生变化的原因 - Open Closed Principle:开闭原则
软件实体对扩展开放,对修改封闭 - Liskov Substitution Principle:里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象
即凡是用到父类的,也能用子类替代,子替代父具有可行性 - Interface Segregation Principle:接口隔离原则
客户端不应该依赖它不需要的接口
类间的依赖关系应该建立在最小的接口上 - Dependence Inversion Principle:依赖倒置原则
上层模块不应该依赖底层模块,它们都应该依赖于抽象
抽象不应该依赖于细节,细节应该依赖于抽象
补充
L 项的扩充
Law of Demeter:迪米特法则
一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话
OOD 过程
回顾先前对于软件工程专业从业人员的定位:向上对接领域专家、需求方,向下对接技术人员
定位之中就暗含了OOD 过程:先对接领域专家(领域类图、活动图),再指导技术人员(详细类图、时序图、状态图、流程图)
开发流程角度下对 OOD 过程的划分
- 需求分析(领域建模):得到领域类图
- 用例建模:得到用例图
- 系统分析:得到时序图、带属性类图、状态图
- 系统设计:得到详细类图(带属性、方法)、数据库表设计
业务流程角度下对 OOD 过程的划分
- 领域类图:体现可见的交互对象及其相互关系(此处的对象为一般语义下的对象而非计算机术语)
- 用例图:体现用户与系统的交互
- 活动图:体现业务流程
业务层与应用层分隔线,下面是应用层
- 类图:体现类成员(属性、方法)以及类间关系
- 时序图:体现类间调用顺序
- 状态图:体现实体状态变化
OOD 发展
如今,OOD 仍作为一种较细粒度的、中小型规模适用的编程思想继续发光发热。而对于更大规模的编程项目(更高抽象层级),则在 OOD 的基础上发展出了大规模和超大规模适用的编程思想,如面向组件(component-oriented)和面向服务(service-oriented)的思想
按照个人理解,大规模甚至超大规模本质上也只是在小规模的基础上,出于规模扩大的需要而进行的多级抽象。不同的编程思想适用于不同的抽象级别。编程思想之间并不是相互独立的,而是相互协同,相互联系着,在其匹配的抽象级别中分别发挥指导作用的
下面尝试按照不同的抽象层级(表现为不同的粒度)对编程思想进行一次汇总
- SO:面向服务,用于超大规模抽象的工具
- CO:面向组件,用于大规模抽象的工具
- OO:面向对象,用于中小规模抽象的工具
- PO:面向过程,用于小规模抽象的工具
- OO:面向对象,用于中小规模抽象的工具
- CO:面向组件,用于大规模抽象的工具
无论是自底向上还是自顶向下的设计,最终表现出来的都是不同层级的一张拓扑图
OOD 实践
如何设计?
设计 = 分析 + 描述,设计就是要反复修改。
分析不完善的设计不是好设计,描述不清楚的设计也不是好设计
图片文字都要用,但一切都是服务于表达,不要为了画图而画图,也不要为了偷懒而用文字
需求分析应该怎么做?
需求分析是写给甲方看的,而不是写给程序员看的,需求分析阶段不应该出现任何计算机相关的术语,且必须用中文(假定甲方是中国人)
需求分析可以采用面向对象或面向过程的分析方法
需求分析的产出(面向过程设计)
- 功能说明书(表)
- 数据流图
- 数据字典
- 开发的硬件(配置)、软件(版本)环境及工具(版本)
需求分析的产出(面向对象设计)
- 用例图
- 总活动图
- 核心类图(领域模型)
- 总时序图
- 状态图
总体设计应该怎么做?
总体设计是写给开发人员看的,可以使用计算机术语,可以用英文方便编码实现
- 分层用例图
- 活动图
- 完整类图
- 详细时序图
- 状态图
详细设计应该怎么做?
设计每个模块内的程序流程
UML 应该怎么画?
领域模型图
领域模型画的不是类,领域模型体现的是业务过程中直接可见的交互对象及其交互关系(此处的对象指的是一般语义角度下的对象,也可以包括环境/场景【如果环境/场景也参与交互的话,环境/场景也可看作对象】,而不是计算机术语中的 Object)可以没有任何属性,其作用只是面向领域专家或客户简单说明交互对象和交互关系
用例图
用例图中只有使用者可以作为参与者,参与者只能是人
补充个人理解:用例图体现的是人与系统(模块)之间的交互
其他资料的描述:参与者可以是人、外部系统或时间
用例间关系
- 用例图中 include 表示箭头指向的用例由所有指出箭头的用例组成
- 用例图中 extend 表示箭头指向的用例可以由指出箭头的用例增强,无论是箭头指向的用例还是指出箭头的用例都是具有完整功能的
- 用例图中 实心三角实线箭头表示指向的用例可以由指出箭头的用例实现,箭头指向的用例是可以不具有完整功能而单独作为接口存在的
类图
核心类图(领域模型)可以只有类名
补充个人经验:其实按照 OOP 的课程要求,领域模型要有类名和属性,不能带方法,带完整方法的在设计类图(即此处的详细类图)
无论是核心类图还是完整类图,不应该出现与其他类无关的类(在图上表现为没有出边或入边的类)
程序初始化完成后就建立起关系的,根据关联程度将关系分为组合和聚合
程序执行时可能体现出关系的,根据关联程度体现为依赖关系和关联关系
如果从这个角度理解,关联关系和组合或聚合关系处于不同的时间领域,不过也存在将组合和聚合关系看做特殊的关联关系的理解方式,即使是后面这种理解方式,能细化到组合或聚合的关系也不会用关联关系表示
状态图与活动图
状态图与活动图的一些符号是一致的,但是二者的应用领域并不相同
状态图:描述实体基于事件反应的动态行为(应用层)
活动图:业务用例实现的工作流程(业务层)
状态图体现的是类或对象的状态变化,不应该出现并行的关系。设想一下,同一事物同时处于同一领域内的多种状态不是很荒唐的事情吗?
活动图中表示分支的小三角本身不能乘载功能,只能体现分支本身,判断过程要单拎出来写。
状态图中的圆角矩形指的是状态(静态),活动图中的圆角矩形指的是活动(动态)
图间关系
需求分析阶段,总时序图和状态图中出现的类或对象一定要在核心类图里面有出现,不然不让用
同理,总体设计阶段的详细时序图和状态图中出现的类或对象一定在要完整类图里面有出现,不然不让用
个人总结
- 用例图:体现用户与系统的交互
- 活动图:体现业务流程
业务层与应用层分隔线,下面是应用层
- 类图:体现类成员(属性、方法)以及类间关系
- 时序图:体现类间调用顺序
- 状态图:体现实体状态变化
个人感受
软件工程专业的人画各种图写各种文档都是为了将抽象复杂的思想(包括需求、设计、实现过程等全流程涉及到的思想,之所以称为脑力工作者就体现在这个方面)进行准确的描述,方便沟通和交流,同时通过留痕给自己留一份保障,避免甲方翻脸不认人或办事不认账,这种规范化的思维可视化就是软件工程师特有的能力和最根本的饭碗。
再结合个人理解,既然画图是为了沟通,那么不可避免地需要对交互方面进行考虑。
说得简单点就是沟通不是单方单向输出的事情,而是多方多向交互(有输入有输出)的过程,不能只管输出不管输入,而交流过程中可控的只有自己,那么我们要做的事情无非就是把事情听明白和把事情说明白。
听明白这块其实自己能控制的方面相当有限,最多做到一些常规的引导和针对混乱的输入进行结构化改造。
说明白这块就不赖别人了,纯个人能力,一方面要练习自己的结构化表达能力(自己说明白),一方面也要考虑到沟通对象的知识水平,保证人家的知识范围能听懂(对方听明白)。理解了这一点就能理解为什么软件专业的学生要画那么多看似在做重复工作的图了,因为软件工程师本身就相当于中间层,要跟两头交互,向上要对接业务领域方面的专家和甲方,向下要对接功能实现方面的开发人员,领域专家和甲方本身不懂计算机术语,开发人员位居幕后也不能直接对接需求(假定对接需求的称为软件工程师,开发人员纯关注功能实现而无需关系业务流程和功能实现之间的对照关系。分工明确其实是有好处的,如果业务分析也要自己做的话,作为开发人员而言说好听点是干的活太多了,说难听点是给的钱太少了),一个功能画两个图突然就显得十分合理了,所以如果是在真实的工作环境中,一块功能两个图不但是可行的而且是必要的。之前觉得麻烦和没必要是因为没有真正考虑到业务这个因素,搞懂软件工程师自身定位很重要。
有效沟通的细粒度划分
-
对方(相对不可控)
-
说明白
-
听明白
-
-
自己(相对可控)
- 说明白
- 听明白