摘录自某PPT
软件建模基础
0 软件质量属性
0.1 如何评价代码质量
-
- 可维护性(maintainability)
对于一个项目来说,维护代码的时间远远大于编写代码的时间。“代码不易维护”就是指,修改或者添加代码需要冒着极大的引入新 bug 的风险,并且需要花费很长的时间才能完成。
- 可维护性(maintainability)
-
- 可读性(readability)
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
代码的可读性在非常大程度上会影响代码的可维护性。
- 可读性(readability)
-
- 可扩展性(extensibility)
代码预留功能扩展点,可以把新功能代码,直接插到扩展点上,而不需要因为要添加一个功能而大动干戈。
- 可扩展性(extensibility)
-
- 灵活性(flexibility)
灵活性是一个抽象的评价标准。
如果一段代码易扩展、易复用或者易用,我们都可以称这段代码写得比较灵活。
- 灵活性(flexibility)
-
- 简洁性(simplicity)
“Keep It Simple,Stupid”
代码简单、逻辑清晰,也就意味着易读、易维护。
思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。
- 简洁性(simplicity)
-
- 可复用性(reusability)
DRY(Don’t Repeat Yourself)
可复用性是很多设计原则、思想、模式等所要达到的最终效果。
- 可复用性(reusability)
-
- 可测试性(testability)
0.2 软件质量属性
(Bass,Clements,and Kazman 2003)
- 可维护性(maintainability)在软件部署之后它能够被更改的程度。
- 可修改性(modifiability)在最初开发期间和最初开发之后软件能够被修改的程度。
- 可测试性(testability)软件能够被测试的程度。
- 可追踪性(traceability)每一个阶段的产品能够被追踪到上一个阶段产品的程度。
- 可伸缩性(scalability)在最初部署之后系统能够成长的程度。
- 可复用性(reusability)软件能够被复用的程度。
- 性能(performance)系统满足其性能目标的程度,例如吞吐量和响应时间。
- 安全性(security)系统抵御安全威胁的程度。
- 可用性(availability)系统能够解决系统失效问题的程度。
1 面向对象
1.0 面向对象知识点
- 面向对象的四大特性:封装、抽象、继承、多态
- 面向对象编程与面向过程编程的区别和联系
- 面向对象分析、面向对象设计、面向对象编程
- 接口和抽象类的区别以及各自的应用场景
- 基于接口而非实现编程的设计思想
- 多用组合少用继承的设计思想
- 面向过程的贫血模型和面向对象的充血模型
1.1 面向对象四大特性
1.1 (封装)
封装(Encapsulation)封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。
封装的意义:如果我们对类中属性的访问不做限制,那任何代码都可以访问、修改类中的属性,虽然这样看起来更加灵活,但从另一方面来说,过度灵活也意味着不可控,属性可以随意被以各种奇葩的方式修改,而且修改逻辑可能散落在代码中的各个角落,势必影响代码的可读性、可维护性。除此之外,类仅仅通过有限的方法暴露必要的操作,也能提高类的易用性。如果我们把类属性都暴露给类的调用者,调用者想要正确地操作这些属性,就势必要对业务细节有足够的了解。而这对于调用者来说也是一种负担。
1.1 (抽象)
抽象(Abstraction)隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。我们常借助编程语言提供的接口类(interface)或者抽象类(abstract)这两种语法机制,来实现抽象这一特性。
抽象的意义:抽象及其前面讲到的封装都是人类处理复杂性的有效手段。在面对复杂系统的时候,人脑能承受的信息复杂程度是有限的,所以我们必须忽略掉一些非关键性的实现细节。很多设计原则都体现了抽象这种设计思想,比如基于接口而非实现编程、开闭原则、代码解耦等。我们在定义(或者叫命名)类的方法的时候,也要有抽象思维,不要在方法定义中,暴露太多的实现细节,以保证在某个时间点需要改变方法的实现逻辑的时候,不用去修改其定义。
1.1 (继承)
继承(Inheritance)继承是用来表示类之间的 is-a 关系,从继承关系上来讲,继承可以分为两种模式,单继承和多继承。单继承表示一个子类只继承一个父类,多继承表示一个子类可以继承多个父类,比如猫既是哺乳动物,又是爬行动物。但在Java和.Net等只能单继承,更提倡面向接口编程。
继承的意义:继承最大的一个好处就是代码复用。假如两个类有一些相同的属性和方法,我们就可以将这些相同的部分,抽取到父类中,让两个子类继承父类。通过继承来关联两个类,反应真实世界中的这种关系,非常符合人类的认知,而且,从设计的角度来说,也有一种结构美感。不过,过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。
1.1 (多态)
多态(Polymorphism)顾名思义,一个接口,多种形态。子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。同样是一个绘图(draw)的方法,如果以正方形调用,则绘制出一个正方形;如果以圆形调用,则画出的是圆形。
多态的意义:只使用封装和继承的编程方式,称之为基于对象(Object Based)编程,而只有把多态加进来,才能称之为面向对象(Object Oriented)编程。多态特性能提高代码的可扩展性和复用性。除此之外,多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。
1.2 面向对象 VS 面向过程
面向对象和面向过程两种编程风格并不是非黑即白、完全对立的。在用面向对象编程语言开发的软件中,面向过程风格的代码并不少见,甚至在一些标准的开发库(比如 JDK、Apache Commons、Google Guava)中,也有很多面向过程风格的代码。
不管使用面向过程还是面向对象哪种风格来写代码,我们最终的目的还是写出易维护、易读、易复用、易扩展的高质量代码。只要我们能避免面向过程编程风格的一些弊端,控制好它的副作用,在掌控范围内为我们所用,我们就大可不用避讳在面向对象编程中写面向过程风格的代码。
1.3 OOA、OOD、OOP
面向对象分析就是要搞清楚做什么,面向对象设计就是要搞清楚怎么做,面向对象编程就是将分析和设计的的结果翻译成代码的过程。
需求分析的过程实际上是一个不断迭代优化的过程。我们不要试图一下就给出一个完美的解决方案,而是先给出一个粗糙的、基础的方案,有一个迭代的基础,然后再慢慢优化。
面向对象设计和实现要做的事情就是把合适的代码放到合适的类中。至于到底选择哪种划分方法,判定的标准是让代码尽量地满足“松耦合、高内聚”、单一职责、对扩展开放对修改关闭等我们之前讲到的各种设计原则和思想,尽量地做到代码可复用、易读、易扩展、易维护。
面向对象分析的产出是详细的需求描述。
面向对象设计的产出是类。
1.3 面向对象设计
划分职责进而识别出有哪些类 根据需求描述,我们把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否归为同一个类。
定义类及其属性和方法 我们识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选出真正的方法,把功能点中涉及的名词,作为候选属性,然后同样再进行过滤筛选。
定义类与类之间的交互关系 UML 统一建模语言中定义了六种类之间的关系。它们分别是:泛化、实现、关联、聚合、组合、依赖。我们从更加贴近编程的角度,对类与类之间的关系做了调整,保留了四个关系:泛化、实现、组合、依赖。
将类组装起来并提供执行入口 我们要将所有的类组装在一起,提供一个执行入口。这个入口可能是一个 main() 函数,也可能是一组给外部用的 API 接口。通过这个入口,我们能触发整个代码跑起来。
1.4 接口 VS 抽象类
抽象类是对成员变量和方法的抽象,是一种 is-a 关系,是为了解决代码复用问题。
接口仅仅是对方法的抽象,是一种 has-a 关系,表示具有某一组行为特性,是为了解决解耦问题,隔离接口和具体的实现,提高代码的扩展性。
什么时候该用抽象类?什么时候该用接口?实际上,判断的标准很简单。如果要表示一种 is-a 的关系,并且是为了解决代码复用问题,我们就用抽象类;如果要表示一种 has-a 关系,并且是为了解决抽象而非代码复用问题,那我们就用接口。
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
1.5 基于接口而非实现编程
应用这条原则,可以将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低耦合性,提高扩展性。
实际上,“基于接口而非实现编程”这条原则的另一个表述方式是&#