软件工程——期末复习(2)
Part1:软件工程基本概念
- 软件=程序+文档+数据
在软件工程中,软件通常被定为程序、文档和数据的集合。程序是按事先设计的功能和性能要求编写的指令序列;程序是完成指定功能的一段特定语言代码。文档是描述程序操作和使用的文档,是与程序开发、维护和使用有关的图文材料;数据是程序运行时需要使用的信息,是使程序能正常操纵信息的数据结构,是软件实现其功能的基础。这三部分相互关联、相互依存,共同构成了完整的软件系统。
- 软件复杂程度的提高给软件开发带来的影响
- 开发难度增加:需要处理的问题和细节大幅增大,可能涉及多个模块和组件的协同工作,增加系统集成的难度
- 开发周期和维护成本增加:需要更多的时间和资源来设计、实现、测试软件
- 质量控制更加困难:软件复杂度程度提高,可能存在大量交互和依赖关系,软件更容易出现缺陷和漏洞
- 安全风险增加:软件复杂程度提高,存在更多潜在的漏洞和攻击面,增加了被恶意攻击的风险。
- 软件工程的定义与工程思想p4~5
工程思想
工程思想是软件工程的基础理念,指的是将工程化的原则应用到软件开发中,以提高开发过程的可管理性、可控性和可靠性。工程思想的关键特点包括:
1. 系统性
软件开发应从系统的角度出发,考虑各个组成部分及其之间的相互关系,形成一个完整的、可操作的系统。每个开发阶段都是全局系统的一部分,必须保证各部分的协调性和一致性。
例子:在开发一个大型电商平台时,开发团队不仅仅关注具体的支付模块,还需要从系统整体的角度考虑与库存管理、订单处理、用户数据等模块的交互。
2. 规范性
软件开发需要遵循一系列标准、方法和流程,以保证开发过程的可控性和可重复性。规范化的开发流程有助于提高团队协作效率、减少错误和提高软件质量。
例子:使用敏捷开发方法、版本控制、代码审查、单元测试等规范化措施,以保证软件的稳定性和可维护性。
3. 可重用性
工程思想强调代码的复用和模块化设计,以降低开发成本、提高开发效率,并增强系统的灵活性和可扩展性。
例子:通过设计通用的API和服务组件,避免重复造轮子,使得相同的功能可以在不同的项目中复用。
4. 过程管理
工程思想强调对开发过程的管理和控制。软件开发过程通常是复杂的、跨团队的工作,因此需要通过明确的流程和规范进行管理,确保项目按计划推进,达到预期目标。
例子:使用项目管理工具(如JIRA、Trello)进行任务分配和进度跟踪,确保开发进度和资源的合理配置。
5. 质量保障
软件工程不仅仅关注软件的开发,还要确保软件的质量能够满足用户的需求。质量保证包括设计、编码、测试、部署等多个环节,确保软件产品能够高效、安全、稳定地运行。
例子:通过进行代码审查、单元测试、集成测试、性能测试等环节,确保软件能够稳定运行,并且满足预期的性能要求。
6. 风险管理
在软件工程中,风险管理是非常重要的。开发过程中可能会遇到技术风险、人员风险、时间风险等,因此需要识别、评估并采取措施应对这些风险。
例子:在一个项目初期识别出潜在的技术难点,提前进行技术验证,或通过迭代开发降低项目的风险。
7. 可维护性
随着软件系统的使用,维护成为了一个重要的环节。良好的工程化实践能够确保软件在后续的生命周期中易于维护、扩展和升级。
例子:设计时考虑模块化、松耦合,编写清晰的文档,遵循代码规范等,有助于未来的维护和升级工作。
把软件当作一种工业产品、采用工程化的方法对软件进行计划、开发和维护
- 软件产品与项目管理p48~49
考核要点:对相应概念的理解,以及综合应用相应概念对相应观点进行辨析。
样例:
Q:试说明“软件=程序+文档+数据”中的文档和建模存在什么样的关系。
答:文档是描述程序操作和使用的文档,是与程序开发、维护和使用有关的图文材料;建模是软件开发过程中一个重要的环节,通过图形化或者文字化的方式描述软件的结构和行为。两者之间相互依赖建模产生的各种图形和文档是文档编写的重要参考,而文档则是对建模成果的详细解释和补充;同时两者是相互补充的,例如:开发人员使用UML图对软件系统进行建模,而文档中的文字描述可以对UML图进行解释和补充,更好的表达软件系统的设计。
Part2:软件开发过程模型
- 软件开发典型阶段及其方法、任务与制品(里程碑)p362
- 软件开发分阶段进行的意义
①降低开发成本:将整个软件开发过程分解为多个独立的阶段,可以更好地控制项目进度,从而降低了开发成本。
②提高开发效率:每个开发阶段都有明确的目标和工作内容,开发人员可以更加
专注地完成自己的任务,从而提高开发效率。
③提高软件质量:通过分阶段进行软件开发,可以更好地控制软件开发流程,提高软件质量。
④确保项目成功:将整个软件开发过程分解为多个独立的阶段,可以更好地控制
项目进度,并及时识别和解决问题,从而确保项目成功。
⑤更好地管理项目:软件开发分阶段进行可以更好地管理项目,帮助管理人员更
好地了解项目进展情况和问题,及时采取措施调整方案,保证项目按计划进行。
综上所述,软件开发分阶段进行是软件开发过程中非常重要的一环,能够提高软
件质量和开发效率,降低开发成本和项目风险,从而最终确保项目成功。
- 典型软件开发过程模型p364-p376
Q:在软件开发过程中分阶段建立里程碑有何意义?
①目标设定和进度控制:里程碑可以帮助团队明确目标,并将开发过程分解为可管理的阶段。
②项目规划和资源分配:每个里程碑代表一个阶段的完成,可以帮助项目经理进行详细的项目规划和资源分配。
③提供可测量的进展指标:里程碑定义了具体的目标和交付物,使得项目进展可以被量化和测量。
④风险管理和问题解决:每个阶段的里程碑标志着一个重要的节点,可以帮助团队及时发现和解决问题。
⑤沟通和协作:里程碑可以作为沟通和协作的重要工具,帮助团队成员之间更好地理解项目进展和目标。
Part3:需求工程
- 需求工程与需求分析p67/69
软件需求的特性:软件需求包括以下六个特性:功能性、可用性、可靠性、性能、可支持性和设计约束
功能性:功能性需求是软件最重要的需求,可分普通功能和全局功能。普通功能泛指软件完成的一个功能或提供的一个服务。全局功能是适用于软件所有应用场景的功能
可用性:泛指能使最终用户方便使用软件的相关需求
可靠性:包括与系统可靠性相关的各种指标,主要有正常运行率、平均无故障时间、平均修复时间、精确度、最高错误或缺陷率等
性能:记录与系统性能相关的各种指标,其中包括对事务的响应时间、吞吐量、容量、降级模式、资源可利用情况
可支持性:定义所有与系统的可支持性或可维护性相关的需求,其中包括编码标准、命名约定、类库以及如何对系统进行维护操作和维护实用工具等
设计约束:设计约束代表以及批准并必须遵循的设计约定,其中包括软件开发流程、开发工具、系统构架、编程语言、第三方构件库、运行平台和数据库系统
需求分析的步骤
软件需求分析一般包括如下的四个步骤:需求获取、需求建模、需求描述(即编写 SRS)和需求验证。
1.需求获取
顾名思义,需求获取就是得到正确的需求信息,常规的需求获取方法有建立联合分析小组、用户访谈与问题分析与确认等。快速原型法也可以用作一种有效的需求分析方法。在分析阶段,开发人员利用快速开发工具先建立一个系统原型,然后让用户参加评估并提出修改意见,进而逐步、准确地确定软件系统的外部行为和特征
2.需求建模
建立分析模型是需求分析的首要任务,是可视化的说明软件需求的最好手段。其中占主导地位的需求建模方法有结构化分析建模和面向对象分析建模两种,这里主要介绍面向对象需求模型
面向对象需求模型由三部分组成:用例模型、补充规约和术语表,其中用例模型又包括用例图和用例规约。用例图主要用于显示软件系统的功能,它包括用例和参与者两方面内容,而用例图下方的用例规约则是对软件系统中每个功能的具体描述。补充规约用于对全局性功能和可靠性、性能等非功能性需求进行文字性描述;后者则用于描述与系统需求相关的术语的定义。基于用例的面向对象需求建模方法,包括画用例图、写用例规约、描述补充规约和编写术语表等四步
3.软件需求描述
编写软件需求规格说明书(SRS),必须用统一格式的文档进行描述,是开发人员在分析阶段需要完成的用于描述需求的文档,包括引言信息描述、功能描述、行为描述、质量保证、接口描述、其他描述
4.需求验证
确保需求规格说明书可作为软件设计和最终系统验收的依据
2、需求的定义、层次与分类 p71/75
(1)需求层次
1.业务需求(描述为什么要开发系统(why))
业务需求是指反映组织机构或客户对系统、产品高层次的目标要求,通常问题定义本身就是业务需求 。
举个例子,背景描述:XX保险公司希望充分利用日益完善的移动通信技术,在原有的办公系统的基础上进行扩展,使得在外的业务人员能够及时地获得客户、业务相关的动态信息,与此同时,实现企业内部的即时通信。
业务需求/目标 :通过该系统的实施,将人工保费续缴、投保手续办理两项业务运转周期缩短10%以上,使企业内部沟通效率大幅改善,以帮助企业运转效率得以提高。
2.用户需求(描述系统能够帮用户做什么(what))
用户需求是指描述用户使用产品必须要完成什么任务,怎么完成的需求,通常是在问题定义的基础上进用户访谈、调查,对用户使用的场景进行整理,从而建立从用户角度的需求。
3.系统需求(描述达到用户要求的具体流程(How))
系统需求(system requirement)用于描述包含多个子系统的产品(即系统)的顶级需求。系统可以只包含软件系统,也可以既包含软件又包含硬件子系统。人也可以是系统的一部分,因此某些系统功能可能要由人来承担。
从系统实现的角度描述的需求。
开发人员(设计及分析人员)在业务需求、用户需求的基础上生成的。
(2)软件需求分类
1.功能需求
定义了开发人员必须实现的软件功能
描述系统应该提供的功能或服务,通常涉及用户或外部系统与该系统之间的交互,一般不考虑系统的实现细节。
描述软件系统所应具有的外部行为
2.非功能需求
描述了系统展现给用户的行为和执行的操作等。它包括外部界面的具体细节、性能要求及质量属性。
非功能需求是产品必须具备的品质,他们可以让产品有吸引力、易于使用、快速、可靠或者安全。
功能性需求是让产品工作的需求,非功能需求是为工作赋予特性的需求。
3.设计约束
设计约束是指对开发人员在软件产品设计和构造上的限制,产品必须遵从的标准、规范和合约。包括:非技术因素的技术选型、预期的软硬件环境和预期的使用环境三大类型。
3、需求分析与建模:UML(用例图与用例描述、概念类图、系统顺序图)
考核要点:对相应概念的理解,以及综合应用相应概念对相应观点进行辨析,能针对具体问题建立需求分析模型(如根据需求完成用例图与用例描述、概念类图、系统顺序图等UML模型的建立)。
样例:
Q:高“吞吐量(Throughput)”是否意味着高“负载(Load)”,请举例说明?试给出自己的判断及理由。
高“吞吐量(Throughput)”并不一定意味着高“负载(Load)”。吞吐量指的是系统单位时间内处理的数据量,而负载则反映了系统资源的消耗程度,如CPU、内存和I/O等。在一些情况下,系统可以保持高吞吐量同时负载较低。例如,采用缓存或负载均衡机制时,系统可以快速处理大量请求,但每台服务器的负载并不高。相反,如果吞吐量很高,系统的负载可能也很大,尤其是当瓶颈在磁盘I/O或网络带宽时。所以,吞吐量和负载之间没有固定的直接关系,它们受系统设计、资源分配及瓶颈等因素影响。
吞吐量(Throughput)
吞吐量通常是指系统在单位时间内成功处理的数据量或请求数。比如,网络带宽中的吞吐量表示每秒钟可以传输的数据量;数据库系统中的吞吐量表示每秒能够处理的事务数量。
负载(Load)
负载则通常指的是系统在运行时所承受的压力或资源消耗。例如,CPU负载表示系统的计算能力被占用的程度,内存负载表示内存的使用情况,或指的是用户请求的数量及复杂度。
吞吐量与负载的关系
高吞吐量不一定意味着高负载,反之亦然。它们之间的关系取决于系统的设计和优化水平。我们可以通过以下几个例子来分析:
1. 高吞吐量但低负载
一个系统如果经过良好的优化,可能在负载较低的情况下仍能实现较高的吞吐量。例如,在分布式系统中,通过合理的负载均衡与缓存机制,可以大幅提高吞吐量,而系统的计算资源消耗保持较低。
例子:一个网站通过缓存静态内容和数据库查询结果,减少了服务器的计算压力和IO操作,使得即便在高并发的情况下,吞吐量仍然很高,但CPU和内存的负载较低。
2. 高吞吐量同时伴随高负载
在一些场景下,为了实现高吞吐量,系统可能需要消耗更多的计算资源和带宽,导致负载也随之增加。例如,在数据中心进行大量数据处理或大规模并发请求时,系统的吞吐量提高的同时,CPU、内存等资源的使用量也会显著增加。
例子:一个处理大量实时交易数据的系统,随着交易数量的增多,它的吞吐量也在增长,但由于系统需要进行大量计算和存储,CPU和内存负载也会变得非常高。
3. 低吞吐量但高负载
有时候,系统的负载很高,但吞吐量却较低。这通常发生在系统资源分配不均或性能瓶颈未解决的情况下。虽然系统正在处理大量任务,但由于资源瓶颈(如数据库锁争用、网络延迟等),吞吐量无法有效提高。
例子:一个数据库系统可能因为过度的磁盘IO或锁竞争而出现高负载,尽管它的吞吐量(每秒处理事务的数量)依然较低。
Part4:软件设计
1、软件设计核心思想:分解与抽象p134
试说明什么是分解(decomposition)和抽象(abstraction),并简要说明为什么分解与抽象是软件设计的核心思想。(课件上的标准答案)
分解是将系统分割为几个相对简单的子系统,以及各子系统之间的关系。
抽象则是通过接口将系统与具体的实现分离,是整个系统的关键所在、本质所在。
因为现代软件设计的核心问题在于控制由于软件复杂性引起的“系统复杂度”,为此“分而治之”是软件设计解决复杂度难题的主要思路,抽象和分解正是“分治”“解耦'的体现,在软件工程实践中也得到了大量应用,故此是软件设计的核心思想。
2、软件体系结构的定义以及对应的软件实现机制p148/p150
定义:软件体系结构由组件、连接件和属性组成,以组件和组件交互的方式定义系统,说明需求和成品系统之间的对应关系,描述系统级别的可伸缩性、能力、吞吐量、一致性和兼容性等属性。
对应的软件实现机制:
1.组件实现:
通过模块化、类、服务等方式实现系统功能。
在微服务架构中,使用服务来实现独立的业务功能。
2.数据流和控制流:
数据在组件之间流动,通过消息队列、函数调用等方式传递。
控制流通过事件或调度机制管理。
3.接口和协议:
组件通过接口和协议(如HTTP、gRPC)进行交互。
4.解耦与模块化:
通过接口实现松耦合,减少组件间的直接依赖。
5.技术栈和框架:
根据架构需求选择合适的技术栈,如数据库、前端框架、容器化技术等。
6.性能优化:
使用负载均衡、缓存机制、并发处理等技术提升系统性能。
7.安全性:
实现身份验证、授权和数据加密等安全机制。
- 体系结构的集成与测试、桩代码与驱动代码的编写p175/p177
1. 体系结构集成
定义:体系结构集成是将各个独立开发的组件、模块或者服务集成到一起,确保它们在整个系统中协调工作,达到预期的功能和性能要求。
集成过程中的关键问题通常包括:
1.组件集成:确保各个系统组件(如前端、后端、数据库等)能够正确协同工作。这可能包括数据库与应用程序之间的集成、API与前端之间的集成等。
2.系统集成:将所有的组件集成成一个完整的系统,确保各部分能够无缝地协作。
3.接口和协议验证:集成过程中需要检查组件之间的接口和协议是否兼容。不同组件之间的接口(如 RESTful API、gRPC等)需要符合规定的协议和数据格式,避免通信失败。
4.集成环境搭建:为了进行集成测试,需要搭建集成测试环境,通常涉及到配置测试服务器、数据库、网络和其他中间件服务。
5.自动化集成:使用自动化工具(如 Jenkins、GitLab CI等)进行持续集成(CI),自动构建、测试和部署集成后的系统。
2. 体系结构的测试
定义:体系结构测试是对系统架构进行验证,确保其符合预期的功能、性能和可靠性要求。
在软件工程中,体系结构测试通常包括以下几个方面:
2.1 单元测试
定义:对系统中最小的独立单元(如函数或方法)进行测试,确保其按预期工作。
工具:JUnit(Java)、PyTest(Python)等。
目标:确保每个单元的功能正确,并且在修改代码时不会引入新的缺陷。
2.2 集成测试
定义:测试多个模块、组件或服务的集成部分,确保它们可以协同工作。
关键点:
测试组件间的接口,确保数据流和控制流能够正确传递。
测试系统不同层级的集成,如前端与后端的集成、服务与数据库的集成等。
验证组件间的消息传递是否符合协议(如 REST API的请求和响应是否正确)。
工具:
Postman(用于测试API接口)。
Spring Test(用于测试Spring框架中的组件集成)。
Mocking(如 Mockito,用于模拟外部依赖组件)。
2.3 系统测试
定义:验证整个系统是否能够按预期工作,涵盖所有集成模块和接口。
目标:确保整个系统在集成后的工作符合用户需求和技术规范。
范围:不仅仅是功能验证,还包括性能、负载、可用性等非功能性测试。
2.4 验收测试
定义:由最终用户或客户进行的测试,用于确认系统是否符合需求和业务目标。
类型:包括功能验收测试、用户界面验收、性能验收等。
关键点:确保系统满足用户需求和期望,通常在系统测试之后进行。
2.5 回归测试
定义:在系统集成或修改后,重新进行的一轮测试,确保新功能或修复不会引入新的问题或破坏现有功能。
关键点:回归测试是确保软件稳定性的重要手段,尤其在大规模系统中,修改一个模块可能影响到其他模块。
2.6 性能测试
定义:测试系统的响应速度、负载能力、并发处理能力等,以确保系统在高负载下依然能够保持良好的性能。
类型:
负载测试:模拟不同用户访问系统,检查系统是否能够承受预期的负载。
压力测试:模拟系统在极端负载下的表现,寻找系统的瓶颈和脆弱点。
稳定性测试:长时间运行系统,检查是否有内存泄漏、崩溃等问题。
2.7 安全测试
定义:确保系统架构符合安全标准,防止潜在的安全漏洞。
类型:
身份验证与授权测试:检查用户权限控制是否严格。
数据保护与加密:确保数据传输和存储过程中的安全性。
漏洞扫描:使用工具扫描系统的安全漏洞,如SQL注入、跨站脚本攻击(XSS)等。
桩代码和驱动代码编写的例子:
//驱动测试
public static void main(String[] args) {
Attendance a=new Attendance();
a.query();
}
//待测试的方法
public void query(){
int value=this.Stub1()+this.Stub2();
System.out.println("我方法的值是:"+value);
}
//桩1
public int Stub1(){
int output=3;
System.out.println("my stub的值是"+output);
return output;
}
//桩2
public int Stub2(){
int output=4;
System.out.println("my stub的值是"+output);
return output;
}
- 典型的体系结构与详细设计模型: UML(包图、构件图、部署图、类图、顺序图)
- 设计质量、模块化与信息隐藏p215/p217/p222
设计质量定义:
设计质量是指在软件系统的设计阶段,所做出的设计决策和设计方案的有效性、可维护性、可扩展性、性能和一致性等特性的综合体现。设计质量直接影响软件系统的结构、模块之间的关系、代码的可读性和可测试性等,进而影响软件的长期可维护性和性能表现。
设计质量可以从以下几个方面来衡量:
功能性
可维护性
可读性和可理解性
性能
可扩展性
可复用性
可靠性和鲁棒性
一致性与规范
可测试性
安全性
模块化定义:
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元。
把代码进行模块化拆分的好处:
① 提高了代码的复用性
② 提高了代码的可维护性
③ 可以实现按需加载
信息隐藏
1.基本思想:每个模块都隐藏了重要设计决策的实现,因此只有该模块的组成部分才知道详细信息:特别是如果存在所有可能的设计更改的列表-隐藏假设列表
2.所有设计决策彼此独立
3.两种常见的信息隐藏:
一是根据需求分配的职责,因为实践表明,需求是经常变化的,频率和幅度都很大;
二是内部实现机制,常见的变化主题包括硬件依赖,输入输出形式,非标准语言特征和库,负责的设计和实现,复杂的数据结构,复杂的逻辑,全局变量。数据大小限制等。
6、耦合/解耦与内聚p219
Part5:软件构造、测试与维护
- 软件构造及代码设计的准则
Open Closed Principle:开闭原则
Dependence Inversion Principle:依赖倒置原则
Single ResponsibilityPrinciple:单一职责原则
Interface Segregation Principle:接口隔离原则
Law of Demeter:迪米特法则
Liskov Substitution Principle:里氏替换原则
Composite/Aggregate Reuse Principle:合成复用原则
一、开闭原则
定义
开闭原则(0pen-ClosedPrinciple,0CP)指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。所谓开闭,就是在不修改原有代码的前提下对原有功能进行扩展。开闭原则是面向对象设计中最基础的设计原则。强调的是用抽象构建框架,用实现扩展细节。它指导我们如何建立稳定灵活的系统,例如系统版本更新,我们尽可能做到不修改源码,但是通过扩展方式增加新功能。
优点:提高软件系统的可复用性及可维护性。
二、依赖倒置原则
依赖倒置原则(DependenceInversionPrinciple,DIP)指设计代码结构时,遵循如下原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象。通过依赖倒置,可以降低类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并降低修改程序带来的风险。
优点:可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险。
三、单一职责原则
定义:单一职责原则(Simple ResponsibilityPrinciple,SRP)指不要存在多于一个导致类变更的原因。简单的理解就是一个模块只做一件事情。庞大的代码肿瘤是代码屎山的重要原因。我们要把所有的模块都小型化,变成一个小的不能再小的积木,这样可以使代码更灵活,更容易维护。假设有一个 C1ass 负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能会导致另一个职责的功能发生故障。这样一来,这个Class 就存在两个导致类变更的因素。
如何解决这个问题呢?我们就要分别用两个Class来实现两个职责,进行解耦。后期需求变更维护互不影响。总体来说就是一个类、接口、方法只负责一项单一的职责。也可以理解为:相同的职责放到一起,不同的职责分解到不同的接口和实现中去,这个是最容易也是最难运用的原则,关键还是要从业务出发,从需求出发,识别出同一种类型的职责。
优点:降低类的复杂度,提高类的可读性,提高系统的可维护性,降低变更引起的风险。
四、接口隔离原则
定义:接口隔离原则(Interface SegregationPrinciple,ISP)指用多个专门的接口,
而不使用单一的总接口,客户端不应该依赖它不需要的接口。这个原则指导我们在设计接口时,应当注意以下几点。
(1)一个类对另一个类的依赖应该建立在最小接口上。
(2)建立单一接口,不要建立庞大臃肿的接口。
(3)尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)。
在设计接口的时候,要多花时间思考,要考虑业务模型,包括还要对以后可能发生变更的地方做一些预判。所以,在实际开发中,我们对抽象、业务模型的理解是非常重要的。
优点:接口隔离原则符合“高聚合、低耦合”的设计思想,使得类具有很好的可读性可扩展性和可维护性。
五、迪米特法则
定义:迪米特原则(LawofDemeter LoD)是指一个对象应该对其他对象保持最少的了解,又叫最少知道原则(Least Knowledge Principle,LKP),尽量降低类与类之间的耦合度。
迪米特原则主要强调:只和朋友交流,不和陌生人说话。出现在成员变量、方法的输入、输出参数中的类都可以称为成员朋友类,而出现在方法体内部的类不属于朋友类。
优点:降低类与类之间的耦合度
六、里氏替换原则
定义:里氏替换原则(Liskov Substitution Principle,LSP)指如果对每一个类型为
T1的对象01,都有类型为T2的对象02,使得以T1定义的所有程序P在所有对象01都替换成02时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。T2是T1的父类,里氏替换原则是继承复用的基石,只有当子类可以替换父类,软件功能不受影响时,父类才能真正被复用。而子类也能在父类的基础上增加新的行为。里氏替换原则是对开闭原则的一个补充。定义看上去比较抽象,我们重新解释一下,可以理解为一个软件实体如果适用于一个父类,则一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。也可以理解为,子类可以扩展父类的功能,但不能改变父类原有的功能。根据这个理解,我们对里氏替换原则的定义总结如下。
(1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
(2)子类中可以增加自己特有的方法;
(3)当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松;
(4)当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类的方法更严格或相等。
优点:①约束继承泛滥,开闭原则的一种体现②加强程序的健壮性,同时变更时也可以做到非常好的兼容性,提高程序的维护性、扩展性。降低需求变更时引入的风险。
- 软件测试与软件质量的关系
软件测试与软件质量的关系:
(1)提高软件质量:软件测试通过发现和修复缺陷,确保软件按预期功能运行,从而提升软件的质量。
(2)衡量软件质量:测试是衡量软件质量的重要手段,测试覆盖度和缺陷密度等指标能反映软件的质量水平。
(3)提高可靠性:测试帮助发现潜在的可靠性问题,确保软件在各种条件下稳定运行。
(4)保证可维护性:通过测试,开发人员可以确保软件的修改不会引入新问题,提高系统的可维护性。
(5)促进用户满意度:测试确保软件满足用户需求和性能要求,从而提高用户的满意度。
总的来说,软件测试是确保软件质量的关键手段,两者相辅相成,测试提升质量,质量反过来影响测试的有效性。
样例:
Q:为什么要“设计易读的代码”?
设计易读的代码是为了让团队成员(包括自己)更容易理解代码的含义,从而更方便地对代码进行修改、维护和扩展。易读的代码有如下一些优点
(1)提高开发效率和可维护性:代码好理解,也就提高了项目的开发效率,也增强了项目的可维护性
(2)便于发现、减少错误:开发人员更容易发现代码中的错误,减少代码中的潜在缺陷和bug。
(3)便于团队合作:易读的代码让团队成员更容易互相理解和协作,从而提高团队的协作效率。
(4)便于代码重用:开发人员可以很快的找到需要的代码,重用起来也方便
综上来说,易读的代码对于提高代码质量、减少错误、提高开发效率和促进团队合作都具有重要的作用,所以我们要设计易读的代码。
其他
- 软件工程方法学:包括三个要素:方法、工具和过程。软件工程方法是完成软件开发的各项任务的技术方法,为软件开发提供了“如何做”的技术;软件工程工具为软件工程方法提供了自动的或半自动的软件支撑环境;软件工程过程则是将软件工程的方法和工具综合起来以达到合理、及时地进行计算机软件开发的目的。
- 为什么进行边界值分析
(1) 等价类划分忽略掉了某些特定类型的高效测试用例,而边界值分析可以弥补其中的一些不足;
(2)编程的很多错误是发生在定义域或值域的边界上。因此针对边界情况设计测试用例,可以更好的检查错误,具有更高的测试回报率;
(3) 边界值数据本质上是属于某个等价类的范围,测试时确实有些重复,但是为了更好的测试质量(边界值特别容易出bug),适当的重复是可以接受的。