在软件发布后,修改软件以修正错误 和提升性能
一些常用的可维护性指标
继承深度 - 表示扩展到类层次结构根的类定义的数量。 层次越深,理解特定方法和字段的定义或/和重新定义的难度就越大。
▪类耦合 - 通过参数,局部变量,返回类型,方法调用,通用或模板实例化,基类,接口实现,在外部类型上定义的字段以及属性修饰来测量与唯一类的耦合。
- 良好的软件设计要求类型和方法应具有高内聚力和低耦合性。
- 高耦合表明设计难以重用和维护,因为它与其他类型有很多相互依赖性。
▪单元测试覆盖率 - 指示自动单元测试涵盖代码库的哪个部分
Five Rules of Modularity Design
Direct Mapping (直接映射) ▪ Few Interfaces (尽可能少的接口) ▪ Small Interfaces (尽可能小的接口) ▪ Explicit Interfaces (显式接口) ▪ Information Hiding (信息隐藏)
耦合是模块之间依赖性的度量。 如果一个模块中的更改可能需要更改另一个模块,则两个模块之间存在依赖关系。
决定于 接口数量 接口复杂度,通讯的复杂度
凝聚力衡量模块的功能或职责的相关程度。 ▪如果模块的所有元素都朝着相同的目标努力,则模块具有高内聚力。
SOLID 面向对象编程原则
▪ (SRP) The Single Responsibility Principle 单一责任原则
引起类变化的原因只有一个,专心做一件事
▪ (OCP) The Open-Closed Principle 开放封闭原则
对扩展是开放的 ,对修改是封闭的,应通过继承和组合改变/扩展功能
当软件系统必须支持一组替代方案时,系统中应该有且只有一个 模块知道方案的详尽列表。
当需要变化时,通过扩展隐藏在接口之后的子类加以完成,而不要修 改接口本身。
▪ (LSP) The Liskov Substitution Principle Liskov替换原则
使用指针或对基类的引用的函数必须能够使用派生类的对象而不知道它”,即子类在用于代替其基类时应该表现得很好(关注的是“操作”的可替换性)
子类型必 须能够替换其基类型)
LSP:对外界看来,父类和子类是“一样”的;
▪ (ISP) The Interface Segregation Principle 接口隔离原则
“客户不应该被迫依赖他们 不使用的接口”,即保持小接口。
– DIP:对接口编程,而不是对实现编程,通过抽象接口隔离变化;
▪ (DIP) The Dependency Inversion Principle 依赖转置原则
高层模块不应该依赖于低层 模块,二者都应该依赖于抽象。
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
类的契约形式化 通过前置和后置条件, 清晰定义子程序的功能
形成测试友好的设计
抽象(abstraction):模块之间通过抽象隔离开来,将稳定部分和容易 变化部分分开
分离(Separation): Keep It Simple, Stupid (KISS) – SRP:按责任将大类拆分为多个小类,每个类完成单一职责,规避变化,提 高复用度; – ISP:将接口拆分为多个小接口,规避不必要的耦合
归纳起来:让类保持责任单一、接口稳定。
GRASP patterns
一般责任分配软件模式(原则),缩写为GRASP,包含为OOP中的类和对象分配责任的准则。Controller
将系统输入事件处理由统一的外观控制器类负责,某个用例的场景控制由用例控 制器负责。
控制器类本身负责收发工作,具体执行通过委托机制进行指派
拥有信息的类负责相应的责任。
Creator
Low Coupling
High Cohesion
Indirection. 将责任 分配给中间对象,以避免直接耦合
Polymorphism根据多态性原则,基于类型定义行为变化的责任被分配给发生这种变化的类型。
Protected variations 防止变异
Pure fabrication 纯虚构e. 设 计虚构的类实现高内聚低耦合和复用
接下来我们看一下设计模式
Factory Method pattern
Static Factory Method静态工厂方法
有名字,易于理解。 通过定义工厂父类负责定义创建对象的公共接口,而工 厂子类则负责生成具体的对象
当一个类不知道它所 需要的对象的类时。在工厂方法模式中,客户端不需要知道具体产品类的 类名,只需要知道所对应的工厂即可。
当一个类希望通过 其子类来指定创建对象时。在工厂方法模式中,对于抽象工厂类只需要提 供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向 对象的多态性和Liskov原则,在程序运行时,子类对象将覆盖父类对象, 从而使得系统更容易扩展。
.将创建对象 的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是 哪一个工厂子类创建产品子类,需要时再动态指定。
Abstract Factory Pattern
提供一个创建一系列相关或相互依赖对象的接口,而 无需指定它们具体的类
使用工厂返回可用于创建相关对象集的 工厂。
抽象工厂模式是对工 厂模式的扩展,允许一个工厂创建更多类型的对象(或者组合更多的对 象构成产品),而工厂模式中一个对象只允许创建一个对象。
一个系统要独立于它的产品的创建、组合 和表示时。 一个系统要由多个产品系列中的一个来配置时。 .当你要强调一系列相关的产品对象 的设计以便进行联合使用时。 当你提供一个产品类库, 而只想显示它们的接口而不是实现时,所有产品以同样的接口出现 ,客户端不需要依赖具体实现。
Builder Pattern
将复杂对象的构造与其表示分开,以便相同 的构建过程可以创建不同的表示
构造算法由单个类(“director”)指定
- 算法的抽象步骤(每个部分一个)由接口(“builder”)指定
- 每个表示提供接口的具体实现( “concrete builders”)
Abstract Factory 专注于产品族的构建 构建过程客户端可见
Builder 构建过程复杂 复杂产 品的构建会不时变化构建过程客户端不可见
二者可以结合完成复杂产品族的构建
Bridge Pattern
将抽象概念与实 现分离,避免过于复杂的继承结构
将类的功能层次结构(abstract)与实现层次结构分离(implementation)
面向客户的逻辑继承结构 面向实现的物理继承结构
Proxy Pattern Motivation
避免对象被客 户端直接访问
远程代理:为 一 个 对 象在 不 同 的 地 址 空间 提 供 局 部 代 表 (缓存机制)
虚代理:根 据需要创建开销很大的对象
保护代理 提供访问保护
Composite Pattern 组合模式
容器中具有基本元素和组合元素两类,如何以统一的方式访问所有的元素
递归方式访问
Observer pattern
观察模式 一种“发布-订阅”形式,发布方的变化,会
通知订阅方
订阅方在发布方 注册 通过 接口分离两者
Abstract subject: maintain list of dependents; notifies them when master changes (the publishers) 维护订阅者列表
Abstract observer: define protocol for updating dependents (the subscribers) 定义更新的协议
Concrete subject: manage data for dependents; notifies them when master changes 维护数据,修改后通知订阅者
Concrete observers: get new subject state upon receiving update message 得到通知后,修改自身的状态
Mediator Pattern
中介模式
中 介者模式中,参与者之间区分不明显(既可以是发送者,也可以是接收者) ,通过mediator作为集中式的hub进行通讯。
r. 观察 者模式中,信息的发送者和接收者划分明确,接收者主要接收发送者发送 的通知和数据。
Visitor Pattern
将数据结构与其上的处理分离
当操作逻辑发生变化时,只需要修改visitor的实现,而不需要修改类。
在多个对象间逐一 传递请求,直到有能够处理的对象执行。
Command Pattern
将请求封装为对象,进而可用不同的请求对客户端进行参数化处 理,对请求进行排队、记录请求日志、支持可撤销的操作。
有时必须向某对象提交请求,但并不知道关于被请求的操作 或请求的接受者的任何信息。
工具箱的设计者无法知道请求的接受 者或执行的操作 ,只有使用工具箱的应用知道该由哪个对象做哪个操作
命令模式通过将请求本身变成一个对象来使工具箱对象可向未指定的应用对 象提出请求。这个对象可被存储并像其他的对象一样被传递。
复合,适配器,桥梁,外墙,代理(结构模式) - 重点:组合对象以形成更大的结构•从旧功能实现新功能,•提供灵活性和可扩展性
▪命令,观察者,策略,模板(行为模式) - 重点: 算法和对象责任的分配•避免与特定解决方案的紧密耦合
▪抽象工厂,构建器(创建模式) - 焦点:创建复杂对象•隐藏复杂对象的创建和组合方式
模块的输入/输出,在内存中存储时为 “串”,在模块间交互时为“流”
语法用于判断合法性,及将字符/字节序列解析为特定数据结构
正则表达 式用于处理字符串,进行拆分、信息提取和转换
解析器生成器根据语法 生成解析器,解析器用于解析字符串
语法中的文字字符串称为终端。终结符/终止符 - 它们被称为终端,因为它们是表示字符串结构的解析树的叶子。 - 他们没有任何孩子,也无法进一步扩展。 - 我们通常用引号编写终端,比如’http’或’:’。
语法由一组产品(产生式)描述,其中每个产品定义非终结符(非终结符)。 - 非终结符就像一个代表一组字符串的变量,而生成作为该变量的定义,就其他变量(非终结符号),运算符和常量(终端)而言。 - 非终结符是表示字符串的树的内部节点
连接(连接),不是由符号表示,而是仅用空格表示:x :: = yz,x是一个y后跟一个z
- 重复(重复),由*表示:x :: = y * x是0或 更多y
- Union(合并),也称为交替,由|:x :: = y |z表示 x或y或z
Optional (0 or 1 occurrence), represented by ?: x ::= y?
an x is a y or is the empty string
– 1 or more occurrences:represented by +: x ::= y+ an x is one or more y (equivalent to x ::= y y* )
– A character class […], representing the length-1 strings containing any of the characters listed in the square brackets: x ::= [abc] is equivalent to x ::= ‘a’ | ‘b’ | 'c‘
– An inverted character class [^…], representing the length-1 strings containing any character not listed in the brackets: x ::= [^abc] is equivalent to x ::= ‘d’ | ‘e’ | ‘f’ | … (all other characters in Unicode)
按照惯例,后缀运算符* ,?和+具有最高优先级,这意味着它们首先应用。
▪接下来应用连接。
▪轮换| 具有最低优先级,这意味着它最后应用。
树的叶子是已经解析的字符串的部分。 - 将叶子连接在一起,我们将恢复原始字符串。
- 主机名和单词nonterminals是树的节点,其树的子树与语法中的规则匹配。
- 主机名等非终结节点的直接子节点遵循主机名规则的模式,单词“。” 字
Markdown一种可以使用普通文本编辑器编写的标记语言,通过简单的标 记语法,它可以使普通文本内容具有一定的格式
对root对应的产生式 右侧,替换其中所有root之外的非终结符(用非终结符的右侧),如果最 终可以得到只有终结符和操作符构成的产生式,则为正则语法
为简单起见,我们假设格式分隔符之间的纯文本不允许使用任何格式标点符号,如_或<。
。 任何单个字符
▪\ d任何数字,与[0-9]相同
▪\ s任何空白字符,包括空格,制表符,换行符
▪\ w任何单词字符,包括字母和数字
▪\。,\(,\), \ *,\ +,…反斜杠转义一个运算符或特殊字符,以便它按字面意思匹配
上下文无关文法左侧只含有一个 非终结符,它定义的语法范畴(或语法单位)是完全独立于这种范畴 可能出现的环境,无需考虑上下文。
不是所有CFG都是正 则的。
程序设计语言的语法多数都是CFG
解析器接受一系列字符并尝试将序列与语法匹配。
▪解析器通常会生成一个解析树,它显示语法产生如何扩展为与字符序列匹配的句子。
- 解析树的根是语法的起始非终结符号。
- 解析树的每个节点都扩展为一个语法生成。
▪表示语言表达式的递归抽象数据类型称为抽象语法树(AST)。
Grammar定义语法规则,Parser generator根据grammar定义的规则 产生一个parser,client利用parser来解析文本,看其是否符合语法定 义并对其做各种处理(例如转成parse tree)
解析器设计模式允许将规则定义为类
. 适用于简单 语法情况,此模式不采用AST的方式解析表达式,节省空间和时间。
State pattern
允许在运行时修改对象的行为和状态,每个行为用一个状态 类表达
状态对象不包含任何属性 修改状态时,修改state object 具体执行的方法,委托给状态对象
Memento Pattern 备忘录模式
.在不破坏 封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状 态。需要的时候,可以恢复到保存的状态。
Table-driven construction
使用表来查询信息而不是使用复杂逻辑语句的模式
以上为第六章的内容,主要讲解了可维护性的常见度量指标 ▪ 聚合度与耦合度 ▪ SOLID ▪ 设计模式:factory method、 abstract factory、builder、 bridge、proxy、composite、 observer/observable、visitor、 state、memento ▪ 语法、正则表达式 ,主要记住正则表达式的用法,还有solid的设计原则。