第一章 概述
1.1 软件 = 程序 + 软件工程
什么是程序?
程序:是指程序员编写的源程序,是建立在数据结构上的一些算法。程序会对数据进行操作,其中既包含静态数据(如软件的图标,提示信息等),也包含动态数据(如程序生成的随机数,网络上下载的数据,用户输入等)。
软件团队不断的修改源代码,如何保证修改过程软件质量不断提高?至少维持以前的质量?
源代码管理(Source Code Control)/配置管理(Software Configuration Management):部分功能可以在不同版本的软件之间迁移;为有些程序配置不同的界面,可以运行在中文、英文等不同语言的操作系统;可以运行在不同版本的操作系统上(如32位版本、64位版本等)。
质量保障(Quality Assurance):通过一系列的工具、流程和文档保证程序的正确性,这些工具、流程本身要达到很高的质量,才能保证开发出来的软件的质量。
软件测试(Test):软件的验证过程。
1.2 软件工程是什么?
软件工程是什么?
软件工程是把系统的、有序的、可量化的方法应用到软件的开发、运营和维护上的过程。
软件工程包括什么?
软件工程包括:软件需求分析、软件设计、软件构建、软件测试和软件维护
什么是软件开发流程?为什么需要软件开发流程?
“软件开发流程”是为了提高软件开发、运营、维护的效率,提高软件质量、用户满意度、可靠性和可维护性而创建的一个体系。该体系包含了很多人们平时在开发、运营、维护软件的过程中使用到的技术、做法、习惯和思想。软件开发流程可以帮助程序员开发出更加优质的软件。
软件的分类
- 系统软件:操作系统、设备驱动程序、工具软件等;
- 应用软件:用来完成特定工作的软件,如浏览器(用于浏览网页)、游戏软件、通信软件等;
- 恶意软件:软件病毒等;
1.2.1 软件的特殊性
(1) 复杂性(Complexity)
大型软件(操作系统、办公系统、搜索引擎)有超过百万行的源代码,上万个不同的文件;软件的各个模块之间有各种显性和隐性的依赖关系,并往往随着系统的成长和模块的增多呈几何级数增长。
(2)不可见性(Invisibility)
工程师“看”不到自己的源代码是如何具体的在机器上执行,商用软件出现错误,工程师可以看到的是程序出错时留下的痕迹(如错误代号、大致的目标代码位置、错误信息等),但是几乎无法完整的重现错误时的场景。
(3)易变性(Changeability)
相比于改变硬件,修改软件要容易的多。人们期待在两种情况下改变软件:1)让软件做新的事情;2)让软件适应新的硬件。但与此同时正确的修改软件也是一件困难的事情。
(4)服从性(Conformity)
软件不能独立存在,需要硬件的支持。同时软件还需要服从系统中其他组成部分的要求,服从用户的要求,服从行业系统的要求(如银行利率变化)。
(5)非连续性(Discontinuity)
连续性主要是指因变量会随着自变量的变化而缓慢变化(中间没有跳跃式的变化),但在很多的软件系统中,往往微小的输入变化会得到截然不同的输出结果,体现出了软件的非连续性。
1.2.2 计算机科学和软件工程的关系
计算机科学和软件工程的不同侧重点
计算机科学 | 软件工程 |
---|---|
发展和研究长期的、客官的真理 | 短期的实际效果(软件会过时) |
理想化的 | 对各种因素的折中 |
确定性、完美、通用性 | 对不确定性和风险的管理,足够好,具体的应用 |
各个学科独立深入研究,做出成果 | 关注和应用各个相关学科知识,解决问题 |
理论的统一 | 百花齐放的实践方法 |
强调原创性 | 最好的、成熟的实践方法 |
形式化,追求简明的公式 | 在实践中建立起来的灵感和直接 |
正确性 | 可靠性 |
计算机科学和软件工程之间的联系
计算机理论的发展会帮助软件工程(例如程序正确性的分析);软件工程的进展(更好的工具,更多的应用领域)会帮助计算机科学家更有效的进行是检验和探索。
一名合格的软件工程师应具备的基本能力
(1)能够研发出符合用户需求的软件:通过实际的工作收集、推导、提炼需求, 并在软件发布后通过实际数据验证需求确实被满足了。
(2)通过一定的软件流程,在预期的时间内发布“足够好”的软件:这个软件是经过全体团队成员的努力,在一个长期阶段逐步完成的。好的产品并不是某个英雄长期加班突击出来的。
(3)能够证明所开发的软件是可维护和继续发展的:需要有对用户分析的详细文档说明,包括对将来发展的分析和计划;主要功能的设计文档和软件的实际行为一致;源代码完整;能用管理软件看到源代码的每次修改记录,Bug的修改过程;具有可以正常执行的单元测试、压力测试脚本等。
第二章 个人技术和流程
2.1 单元测试
为什么需要单元测试?
在团队合作中为了让自己负责的模块功能定义尽量明确;模块内部的改变不会影响其他模块;让模块的质量得到稳定量化的保证。
好的单元测试的标准是什么?
(1)单元测试应该在最基本的功能/参数上验证程序的正确性:单元测试应测试程序中最基本的单元——如C++/C#/java中的类,在此基础上可以测试系统中最基本的功能点。测试API中的每个方法及每个参数。
(2)单元测试必须由最熟悉代码的人(程序的作者)来写:代码的作者最了解代码的目的、特点和实现的局限性,所以最好由程序的作者来书写单元测试。
(3)单元测试后机器状态保持不变:这样就可以不断的运行单元测试。
(4)单元测试要快(一个单元测试的运行时间控制在几秒钟)
(5)单元测试应该可以产生重复、一致的结果:保证错误可以重现
(6)独立性:单元测试的通过与否不依赖别的测试,可以人为的构造数据,保持单元测试的独立性。
(7)单元测试应该覆盖所有的代码路径
(8)单元测试应该集成到自动测试框架中
2.1.3 回归测试(Regression Test)
目的
(1)验证新的代码改正了缺陷
(2)验证新的代码没有破坏模块现有的功能,没有"退化"(Regression)
2.2 效能分析工具
分析方法分类
(1)抽样(Sampling):不需要改动程序,运行较快,可以很快找到瓶颈,但是不能得出精确的数据,也不能准确表示代码中的调用关系树(Call Tree)
(2)代码注入(Instrumentation)将检测代码加入到每一个函数,程序的各种能效数据都可以被精准地测量。但会大大加长运行时间,产生很大的数据文件,也增加了数据分析的时间。同时注入的代码也影响了程序真实的运行情况。
名词解释
调用者(Call):函数Foo()中调用了Bar(),Foo()就是调用者
被调用者(Callee):上述中的Bar()是被调用者
调用关系树(Call Tree):从Main()开始,调用者和被调用者就形成了一个树形关系——调用树。
消逝时间(Elapsed Time):从用户角度来看程序花费的时间
应用程序时间(Application Time):应用程序占用的CPU的时间,不包括CPU在核心态花费的时间
本函数时间(Exclusive Time):在本函数花费的时间,不包括被调用者使用的时间
所有时间(Inclusive Time):包含本函数和调用者使用的时间
第三章 软件工程师的成长
软件工程师如何成长?
(1)积累开发相关的知识,提升技术技能:对编程语言的掌握程度、诊断/提高效能的技术、对设备驱动程序(Device Driver)、内核调试器(Kernel Debugger)的掌握;对开发披平台的掌握程度。
(2)积累问题领域的知识和经验:对某一特定行业(如游戏、医疗、金融等)的了解,一般随着经验的增长,一个工程师可以掌握更广泛、更深入的技术和问题领域的知识。
(3)加深对通用的软件设计思想和软件工程思想的理解。
(4)提升职业技能:职业技能不同于技术技能,主要包括自我管理的能力、表达和交流的能力、与人合作能力、按质按量完成任务的执行力、这些能力在各个行业都很重要。
(5)实际成果:自己参与的产品用户评价如何?市场占有率如何?对用户有多大价值?自己在其中起了什么作用?实际的工作成果是最重要的评价标准。
团队对个人的期望
(1)交流:能有效地与其他队员交流。
(2)说到做到:能够按时交付任务等。
(3)接受团队赋予的角色并按角色要求工作:能否接受不同的任务并高质量完成。
(4)全力投入团队的活动:像一些评审会议,代码复审等活动可以全力以赴的参加,而不是游离于团队之外。
(5)按照团队流程的要求工作。
(6)在参加团队活动之前能做好准备工作。
(7)能够从事实和数据出发,按照流程,理性地工作。