软体工程是计算机科学的一门分支,目的是寻找能引导大型且複杂软体系统开发的原则,开发这样的系统所要面的问题比撰写小型程式大得多,比如开发一个大型系统需要多人一起合作很长的时间,在期间所提出的系统需求也可能会随时调整,并且合作的人员也会有所改变,因此软体工程包含人员与专案的管理,不过这个议题比较像是商业管理领域,本章只着重在计算机科学议题上。
软体工程学
在工程学中由于他是个非常完善的领域,所以会有许多之前就发展好的工程方法可以用来处理问题,但软体开发的特性与工程领域有着基本的不同,其中一项差异是从预先建置好的通用元件来建构系统的能力,当要建构一个複杂的设备时,传统的工程领域长久以来都使用现成的元件作为基本构建,比如要设计一部新车,设计师不需要设计新的引擎或变速箱只要使用那些已经设计好的零件,但在软体工程来说这个部分相当落后,过去设计的软体元件都属于订製款所以很难让别人使用,使得複杂的软体系统通常都是从头开始设计。
另一个软体工程与其他工程的差异是缺乏用来评估软体特性的评量法 (metrics)
,比如要估算软体开发的成本,若想要估算计画中的产品複杂度就缺乏可靠的软体複杂度评估法,同样的评估软体的品质也是一个大挑战。
因此软体工程的研究分为两个层次进行,有些研究者(实务派)发展能够立即应用的开发技术,而其他研究者(理论派)则寻找新的原理或理论,不论那个派系都急需新的进展,软体错误已经发展成会造成严重灾害的程度了,比如误认错的目标为攻击目标或让银行损失个几百万等等。
不过情况也不是非常悲观,电脑科技对于软体发展也产生了所谓的电脑辅助软体工程 (computer-aided software engineering, CASE)
让软体开发程序变得更有效率,有些工具是专门为了软体工程设计的比如说有种名为整合开发环境 (integrated development environment, IDE)
的系统结合了软体发展的工具(编辑器, 编译器或除错工具等等) 成为单一整合工具。
大型系统开发的挑战之一是需要集合众人之力,而团体合作进行一个大型专案的个阶段都需要各种不同的技能,使得程式设计师可以单独撰写程式以降低每个人所负责的任务与複杂性,事实上是因为大型软体系统非常複杂所以要将其分割程序多小型的元件好让每个小元件可让每个程式设计师完全掌控。
软体生命週期
软体工程中最重要的概念就是软体的生命週期。
整体週期
一但软体完成后他就会进入使用与被维护的循环,这个循环会在软体的使用期间不断的重複,许多工厂所製造的产品也有这种循环,不过两这的差异在于其他产品的维护阶段往往是修理的过程,但软体维护阶段包含更正
与更新
,实际上软体维护阶段是因为发现错误或因为软体因应应用上的改变而需要进行调整,或是在上次改版的改变造成其他方面出了问题。
无论什么原因造成软体需要进入维护阶段都会需要安排一个人力去研究其程式码或相关文件,直到了解该程式并找到问题所在,但即使软体设计良好且文件也清楚的情况下,要了解其他人的程式码也是个不容易的工作,因此多数软体工程的研究都专注于软体生命週期的发展阶段希望藉此提高程式效益。
传统软体开发阶段
传统软体开发的生命週期主要步骤是软体需求分析
, 设计
, 实作
与测试
。
软体需求分析
软体生命週期始于软体需求分析,目的是明确定界定软体的服务项目,并指明这个服务的任何条件(时间限制, 安全性等等),其分析包含从相关人士 (stakeholder)
取得重要意见,软体需求分析的过程包含收集与分析软体使用者的需求与软体专案相关人士进行软体期望, 需求, 成本与可行性之间平衡的协商并最后发展出明确的指名软体必须具备的特徵和服务,这些需求应记录在软体需求规格书 (software requirements specification)
的文件中。
从软体开发这的角度来说软体需求规格书应该定义软体开发的明确目标,然而规格书中恨少会有明确的目标,事实上多数软体工程领域的实务派认为沟通不良与软体需求的变动是成本超支与软体延迟交付的主要原因。
设计
软体需求分析对软体产品进行描述,而设计则是对该软体的建构提出规划,所以说需求分析是找出要解决的问题而设计是开发出问题的解法
,软体系统的内部结构是在设计阶段中建立的,设计阶段的结果代表软体系统的结构有清楚的描述。
实作
实作包含程式的撰写
、资档的产生
以及资料库的开发
,在实作阶段可以看出软体分析师 (softwaren analyst)
与程式设计师 (programmer)
任务上的区别,软体分析师参与了软体整个开发过程但会比较注重在需求分析与设计阶段,而软体设计师则主要参与实作阶段。
测试
在整个开发过程的每个步骤中都需要进行準确的测试,测试是确保软体品质的必要程序之一,而确保软体品质是整个软体生命週期的目标,因此许多软体工程师认为测试不该再视为软体开发的一项单独步骤,而是应该融入其他步骤中,因此软体开发程序应该是软体需求分析与确认 (requirements analysis and confirmation)
, 设计与验证 (design and validation)
以及实作与测试 (implementation and testing)
。
软体工程方法论
早期软体工程法坚持以严格且循序的方式来执行分析, 设计, 实作与测试,因此软体工程师坚持在开始设计之前必须先完成整个软体需求规格,同样的在开始实作钱也要先完成设计,这种开发模式称为瀑布模式 (waterfall model)
,因为开发过程只允许按照一种方式进行。
最近软体工程开始有了改变,出现了一种称为渐进模式 (incremental model)
的开发模式,这种模式认为软体可以逐步构建出来,最初所完成的软体是一个有限功能的简化版本,一旦这个版本经过测试并由部分使用者评估过就会以渐进的方式加上其他功能并在进行测试直到系统全部完。
另一种不走瀑布模式的方式称为反覆模式 (iterative model)
,这种模式实际上有时候等于渐进模式,但两者的差异在于渐进模式会『拓展』
每个初步版本使得系统规模逐渐增大,而反覆模式则是『精炼』
每个初步版本,实务上渐进模式会包含反覆测试而反覆模式可能会逐步的加入新功能。
反覆模式法的一个重要实例是统一软体开发过程 (rational unified process, RUP)
,RUP 的广泛使用导致了另一种非营利版本的发展,这种版本称为统一软体 (unified process)
并提供非商业模式的免费版。
渐进模式和反覆模式有时候会利用软体开发往原型化 (prototyping)
的趋势来进行系统建置与评估,原型化指的是非完整版的系统,这个系统称为原型,在渐进模式中这个原型会逐渐演进为完整的终极版,这个过程称为演进式原型化 (evolutionary prototyping)
,而在反覆模式中圆形可能被捨弃使最终设计有新的实作,这种方式称为丢弃式原型化 (throwaway prototyping)
,属于丢弃式原型化的例子之一是快速原型化 (rqpid prototyping)
,这种方式能够在软体开发的早期阶段快速地建构出预想中的系统简单版,其目的并非一个能运作的产品而是作为一种说明工具以用来在软体开发过程中对相关人士阐述系统特性。
在电脑爱好者之间有一种使用多年的方法,称为开放原始码发展法 (open-source development)
,是渐进模式与反宽魔是非正式的典型模式。透过这种方法产生了现今许多免费软体,其中着名的例子就是 Linux
,软体开放原始码的开发过程为: 由一个程式设计师撰写该软体的初始版本然后将程式码与相关文件放到网路上,之后这个软体可以供他人下载使用,因为其他人也可以获得这个软体的原始码与文件,所以可以对他进行修改以符合他们自己的需求过更正使用上发现的错误与问题,然后由开发者将这些更改发布在新的版本中。
与瀑布模式最大相逕庭的方式称为敏捷法 (agile method)
,这是众多方法所集合而成的模式,此模式对软体需求改变进行快速反应并且不重视眼镜的需求分析和设计,简而言之敏捷法举有高度弹性与瀑布式有强烈的对比。
模组化
若要修改软体,程式设计师必需要了解该程式或相关部分的程式码,要了解大型软体系统程式码几乎是非常困难的情况,尤其是软体没有模组化 (modularity)
,所谓的模组化是将软体划分为适当大小的单元,一般称为模组 (module)
,每一个模组都负责软体某部分的功能。
耦合
模组化可以让程式划分为适当大小的单元,因此对程设的修改就只需要针对少数几个模组即可,让程式设计师可以专注于程式的某个部分而不用了解整个系统的程式,不过这也要基于这个模组的修改不影响到其他模组的前提下,因此当要设计模组化系统时应尽力设计出独立的模组
,要尽量避免模组之间的连结亦即模组之间的耦合 (couping)
要尽量的少,软体系统中不同模组之间的耦合程度可以看出这个软体系统的複杂度。
模组之间的耦合有几种不同的形式,其中一种是控制耦合 (control coupling)
,当某个模组使用到同一个资料项则其中一个模组对资料的异动可能会影响到另一个且资料本身的格式异动会同时影响到两个模组。
两个函数之间的耦合有两种不同的形式,其中一种是由某个函数将资料传给另一个函数,这种耦合在结构图中是以箭头
表示,两个函数之间的箭头会标示所传送的资料而箭头方向代表资料传送的方向。
物件导向式程式设计的好处之一是他本质上就能让物件之间的资料耦合发生率降到最低,因为物件中的方法一般都会包含物件内部资料的操作,由于不需要将在物件内部资料传送给其他物件使得物件之间的资料耦合可以减少到最低。
相较于以参数传传送资料的形式,也有资料会以全域资料 (global data)
的形式让模组共用,全域资料可以让系统中所有模组直接取用,使用全域资料要非常小心,因为有人修改到某个全域资料的话会很难发现其他模组的更改状况进而导致意想不到的错误出现。
内聚
与最小化耦合等同重要的就是最大化模组之间的内部连结这种连结称为内聚 (cohesion)
,用来表示内部的连结换句话说内句是模组内部元素的相关性,如果有模组需要修改它内部的结构,可能会造成整体程序上的混淆或出意料之外的错误,因此除了要降低模组之间的耦合之外也要提高模组的内聚。
比较弱的内聚称为逻辑内聚 (logical cohesion)
,这种内聚是由模组内部的元素做都执行类似的操作所形成,而比较强的内聚称为功能内聚 (functional cohesion)
亦即模组内的所有元件都只专注在单一操作,在命令式设计中可以将子任务分中到其他模组以增强功能内聚,而在物件导向程式中肘个物件通常只有逻辑内聚
,因为物件内的方法通常会执行不相关的操作,唯一共通的连结是这些方法的操作都由同一个物件执行。
软体设计师应该要尽力的让物件中的每个方法都具有功能内聚,也就是将物件本身只有逻辑内聚而他内部的每个方法应该只执行一个功能内聚的任务。
资料隐藏
模组设计的要法之一是掌握资讯隐藏 (information hiding)
的概念,它是指将资讯侷限于软体系统的某个特定区域中
,这个隐藏的资料可以包含资料、资料型别、编码法、模组的内部组成结构、程序单元的逻辑以及任何与模组内部特性相关的其他要素。
资讯隐藏的目的是避免模组的运作与其他模组产称生不必要的相依性或影响,不然可能会影响到其他正常工作的模组或被错误的模组影响到,要注意的是资讯隐藏有两种意义,一种是作为设计目的另一种是作为实作目的,模组的设计应该使其它模组不需要去存去其内部资源
,并且模组的实作应该加强其界限,前者的例子就是要最大化内聚
以及最小化耦合
,而后者的例子包含使用区域变数
、资料封装
以及使用定义完善的控制结构
。
元件
上面有提到软体工程的领域中缺乏预先订製好的构建已建立出大型软体系统,不过软体的模组化让这个问题得以实现,因为物件导向的物件可以构成完整的程式并且具备清楚的介面让个物件可以彼此沟通,不过虽然物件和类别可以作为软体工程所需要的构建但不代表他们是完美的,其中一个问题是他们只能提供相对小型的构建,物件实际上只是元件 (component)
的特例,元件具有更一般化的概念是软体中可重複使用的单元,实际上大多数元件都是基于物件导向程式法并且有一个以上的物件且都可以独立完成某项工作。
交易工具
本章会介绍一些软体开发中分析与设计阶段会用到的塑模法
与符号法
。
版本控制: 版本中系统现在已经是大多数软体工程的一部分,透过网页和网路的版本控制系统可提供一致的机制来追蹤程式码的修改,让多人开发合作编写某个大型系统时可以更好的维护与开发,常见的版本控制工具有
Git
,Subversion
,Mercuurial
等等,版本控制可以精确追蹤是谁在何时做了哪些更改,且可以回朔原始码到较早期的版本用于修改不当更改造成的错误或影响。
老朋友
资料流程图 (dataflow diagram)
是研究资料流向所取得的资讯的表现,箭头代表资料的路径,椭圆代表资料处理的位置点而矩形代表资料的来与和储存。
资料流程图不仅能在设计阶段辅助辨认程序,在分析阶段尝试理解整个系统时也有很大的用处,建构资料流程图可以加强客户与软体工程师之间的沟通。
另一种长年被使用的工具是资料字典 (data dictionary)
,这是用来储存整个软体系统中所用到的资料项目资讯,这个资讯里面有资料的名称
, 资料的有效构成元素 (资料只能是数字还是字母?数值的值域等等)
, 资料的储存位置
以及资料使用在系统的什么地方
。
建构资料字典的目的之一是强化软体工程师与相关人士的沟通,因为资料字典可以在分析阶段中帮忙确认一些问题而不用在设计或实作阶段才发现,他的另一个目的是为了建立系统的一致性。
统一塑模语言
资料流程图和资料字典是在物件导向出现前就存在的软体工程工具,都是基于命令式程式法所发展出来的,而现代的工具称为统一塑模语言 (Unified Modeling Language, UML)
,主要是随着物件导向盛行而发展的,其中一个工具称为使用案例图 (use case diagram)
,他以一个大矩形来表示系统并描绘系统与使用者之间互动关係,这些互动称为使用案例 (use case)
并以椭圆来表示使用案例,而系统使用者也称为行动者 (actor)
会以火柴人表示,
使用案例图是从外部检视软体洗桶架构,UML 也提供许多工具以描述系统内部的物件导向架构设计,其中一种称为类别图 (class diagram)
,这是一种表示类别结构和类别之间的关係符号表示法,类别关係在 UML 的术语中称为关联性 (association)
。
以上面的图来说,通常会以矩形表示 Class
并且以线条表示关联性
,关联性线条不一定会有文字,除了指名类别之间的关联性之外类别图也能表达这些关联性的数量,换句话说类别图可以指名有多少个类别的实例与另一个类别的实例具有关联性,关联性的数量只会有三种形式 一对一关係
, 一对多关係
, 多对多关係
。
类别图代表静态的程式设计特性,但不代表执行时事件发生的顺係,为了表达这种动态的特性 UML 提供许多不同的图形表示法,这种表示法称为互动图 (interaction diagram)
,互动图之一是顺序图 (sequence diagram)
,他会画出执行某项动作所涉及到的个体之间的沟通,他们都以矩形表示个体并有虚线向下延伸,每个矩形与其虚线称为生命线 (life line)
,每个个体之间个沟通已箭头表示并连结彼此的生命线,其中会用文字标示请求的动作,这些箭头按照时间顺序从上而下看而顺序图会被一个大矩形包围这个矩形称为框架 (frame)
。
设计模式
设计模式 (design pattern)
是软体设计中一种预先开发好的模式,用来解决经常出现的问题,比如转接器模式 (Adapter pattern)
所提供的方法可以用来解决使用预先订製好的模组来建构软体时会发生的问题,简单来说如果有个模组可以用来解决目前手中的问题,但这个模组没有与目前应用程式相容的介面,这时就可以使用转接器模式将这个模组包在另一个模组里面,这样就能被应用程式使用。
品质保证
软体故障、成本超支和开发逾时等问题会在开发软体系统中不断的发生,这些问题都需要有方法来改善软体的品质控制,本章将会介绍这个议题以及过去的一些成果。
软体测试
现今大多数软体是以测试来进行验证,但除非我们可以测试到所有可能发生的状况不然即使是做简单的程式依然会有错误发生,因此测试所有可能性几乎是不可能的任务。
白箱测试 (glass-box testing)
基于帕雷托法则与基础路径测试的方法都需要了解软体内部的组成,因此这两种方法被归类在白箱测试 (glass-box testing)
中,亦即软体测试者需要了解软体内部结构并根据对软体的了解来进行测试。
帕雷托法则 (Pareto principle)
不过软体工程师发展出其他测试的方法能够以有限个数的测试提高发现错误的机率,其中一种方式是基于软体错误倾向再一起,亦即经验告诉我们在大型系统中有少部分模组比较容易发生问题,藉由找出这些模组且充分测试这些模组就可以找到系统中大部分的问题与错误,这样要比全面的测试所有模组来的有效率,这种方式就是帕雷托法则 (Pareto principle)
的实例之一,只要在关键区域多付出一些心力其效就可以快速增加。
基础路径测试 (basis path testing)
另一种方法称为基础路径测试 (basis path testing)
,这是用来确保软体中每个指令至少都能执行到一次,这种方式使用数学中的图轮法,虽然不能保证每种状况都会被测试到但可以确保软体中每一条陈述在测试过程中都会至少被执行到一次。
黑箱测试 (black-box testing)
这种测试会在不了解软体内部组成的情况下进行,简而言之黑箱测试是从使用者角度出发,在黑箱测试中测试者并不会考量软体是如何运行的,仅在意软体是否能正确且即时的执行。
边界值分析 (boundary value analysis)
这是黑箱测试的方式之一,这种方式会包含一组指定範围的资料,这些资料称为等价类 (equivalence class)
,软体以相同方式来执行这些资料人后再以接近边界值的资料来测试软体, 举例来说如果某软体的输入值有特定的範围那么就应该要使用这个範围的最大值与最小值来测试软体,或着若是某软体负责协调多项作业那么就应该要以最多可能的运作项目才测试软体,若软体在这些测试项目中都能正常执行那么软体也应该能够在所有项目中正常执行。
beta 测试 (beta testing)
将软体的前期版本给一群有兴趣的使用者进行测试,目的是在软体的最终版本还未稳固并上市之前可以知道软体在实际应用上的状况,如果测试是由开发端进行测试的话则称为alpha 测试 (alphq testing)
,beta 测试的好处远大于传通的错误寻找法,从一般用户反馈的意见来修改软体以达成调整行销策略的目的。
文件
如果使用这无法知道软体该怎么使用或维护那么这个软体就没有用处,所以文件算是软体套件很重要的一部分,软体文件有三个主要的方向分别对应文件的三种类别: 使用者文件
, 系统文件
和技术文件
。
使用者文件 (user documentation)
使用者文件 (user documentation) 其目的是说明软体的特色
与使用方式
,以应用层面的术语撰写让使用者阅读的,好的使用者文件通常结合了设计完善的使用者介面,可以增加软体的销售量。
系统文件 (system docummentation)
系统文件 (system docummentation) 其目的是描述软体内部的组成以利于软体在日后的维护,系统文件的主要内容是系统所有的程式码,其中这些程式码必须要易于阅读,这也是为什么现代软体会使用高阶语言并使用注释以及使用模组化设计的原因。
系统文件的另一个要素是设计文件的纪录,该纪录包含软体需求规格书以及这些规格设计的过程,在软体的维护中这些资讯非常有用,因为他说明了这些软体是如何设计的。
技术文件 (technical documentation)
技术文件 (technical documentation) 其目的在说明如何安装与维护软体(如调整软体运作参数、安装更新以及回报错误等等)。
软体所有权与责任
法律上赋予软体所有权的範畴归类于智慧财产权法 (intellectual property)
,着作权与专利权的目的在于让产品的开发者可以在公开产品的同时取得所有权的保障,因此产品的开发者都会在相关产品上加上所有权的声明,包含需求规格书、设计文件、程式码、测试规划以及最终产品的显眼处,版权声明会清楚的指名所有权,此外开发者权益会在软体授权书 (software license)
上以法律用语正式的陈述,软体授权书是软体产品所有权人与使用者之间的法律协议,赋予使用这某些权限来使用该产品而不需要转让其智慧财产权给使用者,这个协议会详细的说明双方的权益与义务。
专利法是为了让发明者可以从发明中获取利润,为了取得专利发明者需要证明该发明是新颖、有用且与其他发明者之发明无相似之处的,专利权年限一般是专利提申请后的 20 年。