本文参考自 Software Engineering : A Practitioner’s Approach — 8th edition by Roger S. Pressman
12、软件设计概念
需求与设计
- 需求和设计之间的界限不明显
- 区别大概是(what to do 和 how to do)
模块化 (modularity)
- (类似程序里 function 的概念)
- 模块不要太少,也不要太多,因为会有 integration cost
内聚性 (cohesion)
- 越高越好
- 内聚性描述一个模块的各机能凝聚的状态或程度
- 理想状况下,一个内聚的模块只实现单一独立的功能
耦合性 (coupling)
- 越低越好
- 耦合性测量模块之间的信息交互量
- 高耦合性使得模块不易维护、不易测试、不易复用以及难以理解
面向切面编程
(AOP 是 Aspect Oriented Programming 的首字母缩写)
面向切面编程
13、架构设计 (architectural design)
如同楼房建筑需要架构设计,软件也需要整体架构设计
data-centered architecture
data-flow architecture
call-and-return architecture
layered architecture
13.6 上下文 (context) 分析
- 确立上下文
- 软件会和外部实体 (其他系统、设备、人员) 进行交互
- 上下文分析帮助码农认识这个软件的边界
上下文图示
软件架构例子
14、构件级设计 (component-level design)
构件级设计发生在第一次架构设计 (上一章) 完成之后。这时,笼统的数据和程序结构已经建立,需要完善各种细节,把模型草稿转化为能运行的软件。
14.1 什么是构件?
在架构中加入构件
OOP例子:上部和中部是构件级设计的输入,下部是构件级设计的输出
之所以要把构件连接 interface,是因为 interface 定义了当前构件与其他构件的接口,比如为 PricingTable 构件提供
computePageCost()
服务接口,为 JobQueue 构件提供checkPriority()
服务接口。这俩接口对应图中的两个“棒棒糖”。
14.2 OOP 构件设计
14.2.1 各种基本设计原则
开放-封闭原则 (OCP)
英文是 Open-Closed Principle (OCP)
一个构件应该方便向外拓展、增加功能(开放),但是不需要对内修改。为了达到这一点,就定义通用的程序接口。
比如对于狗
、鸡
和猫
,定义通用的 interface 动物行为
,包含方法叫
和跑
。那么这个动物行为
就是封闭的,而狗
、鸡
和猫
等各种动物就是开放的。
里氏替换原则 (LSP)
Liskov Substitution Principle (LSP)
程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的。或者说,“只有在确定是 is-a 的关系时才能使用继承”
比如鸟类
包含方法飞
,如果鸵鸟
继承了这个类,就违反了这个原则。正确的方法是从鸟类
中分离出一个不会飞的不会飞的鸟类
。否则,鸵鸟
的飞
将是未知行为
接口分离原则 (ISP)
Interface Segregation Principle (ISP)
一个接口或者类应该拥有尽可能少的行为。接口包含太多的方法会降低其可用性,这种包含了无用方法的”胖接口”会增加类之间的耦合。如果一个类想实现该接口,那么它需要实现所有的方法,尽管有些对它来说可能完全没用。
如果一个接口包含了过多的方法,应该通过分离接口将其拆分。
依赖倒置原则 (DIP)
Dependency Inversion Principle (DIP)
这个原则的要求是:高层模块不应该依赖底层模块,两者都应该依赖其抽象。其实又是“面向接口编程,不要面向实现编程”的内在要求。
15、UI 设计
15.1 三条黄金定律
- 简化系统,让用户具备掌控系统的能力
- 减少用户的记忆负担
- 使多个用户界面保持一致
15.2 UI 设计流程
- (1) 界面分析与建模
- 类似 8-11 章的需求分析 可以使用 use case 和 swimlane diagram
- (2) 界面设计
- 使用 use case
- (3) 界面实现
- 细节包括:响应时间、出错管理、帮助、控制菜单
- (4) 界面 validation
- (5) 螺旋模型,重复1-4
16、基于模式的设计
本章提供一些现有的、可重复的设计思路(称为设计模式 / design pattern),可以借鉴到新的项目中。
设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。
16.1 设计模式
设计模式包含 背景-问题-答案 三位一体。
创建型模式
模式名称 | 英文 | 描述 | 图解 |
---|---|---|---|
抽象工厂 | abstract factory | 为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候,可以从抽象工厂中选出相应的系列创建一个具体的工厂类。比如右图展现了 Win 和 Mac 产品族的 Button 系列 | ![]() |
结构型模式
模式名称 | 英文 | 描述 | 图解 |
---|---|---|---|
适配器 | adapter | 适配器模式可以消除由于接口不匹配所造成的类兼容性问题,做法是将目标接口包裹在一个已存在的接口中。 | ![]() |
桥接 | bridge | 将一个抽象与实现解耦,以便两者可以独立的变化。在右图中,Refined Abstraction 和 Concrete Implementor 实现了解耦。 | ![]() |
组合 | composite | 把多个对象组成树状结构来表示局部与整体,这样用户可以用 component 来相同对待“单个对象 (leaf)”和“多个对象的组合 (composite)”。 | ![]() |
代理 | proxy | 当一个复杂对象的多份副本须存在时,副本使用代理来节省空间。比如右图中 Real Subject 是一个超大的音频文件,那么 Proxy 只是一个快捷方式,并不直接存储音频文件的内容。 | ![]() |
行为型模式
模式名称 | 英文 | 描述 | 图解 |
---|---|---|---|
命令 | command | 这种模式尝试以对象来封装实际行动及其参数,于是这些行动可以被:重复多次、取消、取消后重做、排列运行。这些都是现代大型应用程序所必须的功能。 | ![]() |
策略 | strategy | 定义一个算法的系列,将其各个分装。这本质上就是对同一个抽象进行多种实现。 | ![]() |
观察者 | observer | 或被称作发布/订阅模式,观察者模式定义了一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。这样可以在维持一致性的同时减少耦合度。 | ![]() |