【软工】软件工程基础知识【第一版】


软件工程基本概念

软件工程的目标与常用模型

  1. 软件工程的目标:提高软件的质量与生产率,最终实现软件工业化生产
  2. 软件工程的主要环节
  3. 软件工程的线性模型
  4. 线性模型又称瀑布模型,但是太过理想化,抗风险能力太弱了,偶尔被提起也是属于被贬对象
  5. 但应该认识到线性思维确实人们最容易掌握的思想方法,遇到非线性问题时,人们总是想尽办法转换成线性问题,因此尽管线性模型被抛弃了,但是线性思维仍然需要领会
  6. 对于一个优秀的程序员不能总是套用固定的模型,例如程序设计不是总是先设计后测试,往往是把测试分为同步测试和总测试,即边做边测试,最后再测试

软件开发的基本策略

  1. 复用,文人简称为拿来主义,即过去做对的东西要保留下来,笔者也是因为这个原因针对博客推行版本更新,成果积累制度,不然每次都要从头再来这个效率就太低了
  2. 面向对象的口头禅:不要发明相同的车轮子
  3. 软构件:将具有一定集成度并可以重复使用的软件组成单元
  4. 软件复用可以表述为:构造新的软件系统可以不必每次从零做起,直接使用已有的软构件,即可组装(或加以合理修改)成新的系统
  5. 软件复用有可能从别人那里拿来,就有可能从自己这里被拿去,因此必须要保证方便
  6. 利用软构件生产应用软件的过程
  7. 分而治之:即一块蛋糕一口吃不下,分成几块来吃
  8. 软件人员执行分为治之时应当考虑:复杂问题分解后能否用程序实现,最后组成一个软件系统解决复杂原始的问题
  9. 优化:软件的优化是指优化软件的各个质量因素,如提高运行速度,提高对内存资源的利用率,使用户界面更加友好,使三维图形的真实感更强等等。就好比造武器,火力总是走向又猛又轻巧
  10. 但是优化并不一定要把事情做好,因为软件之间总是存在千丝万缕的联系,往往牵一发而动全身,不能所有的目标都得到优化的时候,就需要折中
  11. 折中策略笔者的理解就是集体主义往往比个人英雄主义强,我们必须要考虑到整体的优化,不能执着于某一个地方过于加强,但是折中不能滥用,否则容易造成拖鞋,这是两回事情

不正确的观念

观念之一:我们拥有一套讲述如何开发软件的书籍,书中充满了标准与示例,可以帮助我们解决软件开发中遇到的任何问题。

客观情况:好的参考书无疑能指导我们的工作。充分利用书籍中的方法、技术和技巧,可以有效地解决软件开发中大量常见的问题。但实践者并不能因此依赖于书籍,这是因为:

(1)现实的工作中,由于条件千差万别,即使是相当成熟的软件工程规范,常常也无法套用。

(2)软件技术日新月异,没有哪一种软件标准能长盛不衰。祖传秘方在某些领域很吃香,而在软件领域则意味着落后。

观念之二:我们拥有最好的开发工具、最好的计算机,一定能做出优秀的软件。

客观情况:良好的开发环境只是产出成果的必要条件,而不是充分条件。如果拥有好环境的是一群庸人,难保他们不干出南辕北辙的事情。正所谓往往学渣文具多

观念之三:如果我们落后于计划,可以增加更多的程序员来解决。

客观情况:软件开发不同于传统的农业生产,人多不见得力量大。如果给落后于计划的项目增添新手,可能会更加延误项目。因为:

(1)新手会产生很多新的错误,使项目混乱。

(2)老手向新手解释工作以及交流思想都要花费时间,使实际开发时间更少。所以科学的项目计划很重要,不在乎计划能提前多少,重在恰如其分。

观念之四:既然需求分析很困难,不管三七二十一先把软件做了再说,反正软件是灵活的,随时可以修改。

客观情况:对需求把握得越准确,软件的修修补补就越少。有些需求在一开始时很难确定,在开发过程中要不断地加以改正。软件修改越早代价越少,修改越晚代价越大,就跟治病一样道理。

争议性观点

争议之一:如果软件运行较慢,是换一台更快的计算机,还是设计一种更快的算法?

参考观点:如果开发软件的目的是为了学习或是研究,那么应该设计一种更快的算法。如果该软件已经用于商业,则需谨慎考虑:若换一台更快的计算机能解决问题,则是最快的解决方案。改进算法虽然可以从根本上提高软件的运行速度,但可能引入错误以及延误进程。技术狂毫无疑问会选择后者,因为他们觉得放弃任何可以优化的机会就等于犯罪。

类似的争议还有:是买现成的程序,还是彻底自己开发?技术人员和商业人士常常会有不同的选择。

 

争议之二:有最好的软件工程方法,最好的编程语言吗?

参考观点:在软件领域永远没有最好的,只有更好的。能解决问题的都是好方法或是好语言。程序员在最初学习 Basic、Fortran、 Pascal、C、C++等语言时会感觉一个比一个好,不免有喜新厌旧之举。而如今的 Visual Basic、Delphi、Visual C++、Java 等语言各有所长,真的难分优劣。开发人员应该根据客观条件,选择自己熟悉的方法和语言,才能保证合格的质量与生产率。

程序设计是自由与快乐的事情,不要发誓忠于某某主义而自寻烦恼。

 

争议之三:编程时是否应该多使用技巧?

参考观点:就软件开发而言,技巧的优点在于能另辟蹊径地解决一些问题,缺点是技巧并不为人熟知。若在程序中用太多的技巧,可能会留下隐患,别人也难以理解程序。鉴于一个局部的优点对整个系统而言是微不足道的,而一个错误则可能是致命的。作者建议用自然的方式编程,少用技巧。

 

争议之四:软件中的错误是否可按严重程度分等级?

参考观点:在定量分析时,可以将错误分等级,以便于管理。微软的一些开发小组将错误分成四个等级

一级严重:错误导致软件崩溃。

二级严重:错误导致一个特性不能运行并且没有替代方案。

三级严重:错误导致一个特性不能运行但有替代方案。

四级严重:错误是表面化的或是微小的。

可行性研究

  1. 可行性分析是要决定“做还是不做”。需求分析是要决定“做什么,不做什么”。
  2. 即使可行性分析是客观的、科学的,但决策仍有可能是错误的。因为决策者是人,人会冲动,有赌博心态。
  3. 经济【有没有钱赚】
    1. 成本收益分析:
      1. 人都是无利不起早的,大家参加工作不可能用爱发电,所以收益是第一位的
      2. 如果是为客户做软件项目,那么收益就写在合同中。如果是做自己的软件产品,那么收益就是销售额
      3. 如果做的是小本生意,那可得对成本进行细算。软件的成本不是指存放软件的那张光盘的成本,而是指开发成本。
    1. 经济——短期长远利益分析
  1. 技术【做得了吗?做得好吗?做得快吗?】
    1. 在给定的时间内能否实现需求说明中的功能。如果在项目开发过程中遇到难以克服的技术问题,麻烦就大了。轻则拖延进度,重则断送项目。
    2. 软件的质量如何?有些应用对实时性要求很高,如果软件运行慢如蜗牛,即便功能具备也毫无实用价值。有些高风险的应用对软件的正确性与精确性要求极高,如果软件出了差错而造成客户利益损失,那么软件开发方可要赔惨了。
    3. 软件的生产率如何?如果生产率低下,能赚到的钱就少,并且会逐渐丧失竞争力。在统计软件总的开发时间时,不能漏掉用于维护的时间。软件维护是非常拖后腿的事,它能把前期拿到的利润慢慢地消耗光。如果软件的质量不好,将会导致维护的代价很高,企图通过偷工减料而提高生产率,是得不偿失的事。
  1. 社会环境【有没有人用】社会环境的可行性至少包括两种因素:市场与政策。

需求分析

需求分析的困难

  1. 客户说不清楚需求:有些客户对需求只有朦胧的感觉,当然说不清楚具体的需求。笔者过去发现一家外包公司对接过一家律所,因为律所的老板怎么都说不清楚,导致了一个仅仅是校园毕设的技术项目,做了三年没做完
  2. 需求自身经常变动:据历史记载,没有一个软件的需求改动少于三次。唯一只改动需求两次的客户是个死人。这个可怜的家伙还是在运送第三次需求的路上被车子撞死的,但是办法总比问题多,可以提供以下几种解决方案
    1. 看清楚合同,避免暧昧的说法
    2. 尽量把软件设计成可变动性比较强的,最极端的就是超能陆战队里的微型机器人,无论什么东西都能拼出来,虽然这不可能
    3. 尽可能地分析清楚哪些是稳定的需求,哪些是易变的需求。以便在进行系统设计时,将软件的核心建筑在稳定的需求上,否则将会吃尽苦头
  1. 分析人员和客户理解有误,程序员往往理科强,文科弱,笔者接触的很多程序员连基本的话都说不清楚,因此技术和自然语言上的鸿沟就很难弥补

如何进行需求分析

  1. 两个核心问题开展需求分析:(1)应该了解什么?(2)通过什么方式去了解?
  2. 应该了解什么
    1. 最好为每个需求注释“为什么”,这样可让程序员了解需求的本质,以便选用最合适的技术来实现此需求。
    2. 需求说明不可有二义性,更不能前后相矛盾。如果有二义性或前后相矛盾,则要重新分析此需求。
  1. 通过什么方式去了解
    1. 直接与客户交谈。如果分析人员生有足球评论员的那张“大嘴”,就非常容易侃出需求。
    2. 有些需求客户讲不清楚,分析人员又猜不透,这时就要请教行家。有些高手真的很厉害,你还没有开始问,他就能讲出前因后果。让你感到“听君一席言,胜读十年书。”这里笔者忍不住要吹牛了,因为笔者参与过和律所对接的业务,由于笔者通过了法律职业资格证书,所以对于法律业务的软件开发易如反掌
    3. 有很多需求可能客户与分析人员想都没有想过,或者想得太幼稚。要经常分析优秀的和蹩脚的同类软件,看到了优点就尽量吸取,看到了缺点就引以为戒。前人既然付了学费,后人就不要拒绝坐享其成。

系统设计

  1. ppt画完了当然就要动手啦,系统设计是把需求转换为软件系统最重要的环节
  2. 系统设计一共有四个方面:体系结构,模块结构,数据结构与算法,用户界面
    1. 系统结构相当于骨架
    2. 模块结构相当于人体器官
    3. 数据结构与算法相当于血脉和神经【所以同学们明白为什么面试对算法要求这么高了吗,因为少个器官或许还不一定有大事,但是少跟神经那就麻烦大啦】
    4. 用户界面相当于人的外表

体系结构设计

  1. 体系结构是软件系统中最本质的东西
    1. 体系结构是对复杂事物的一种抽象。良好的体系结构是普遍适用的,它可以高效地处理多种多样的个体需求。
    2. 体系结构在一定的时间内保持稳定。
  1. 层次结构:有些事情比较复杂,我们没法一口气干完,就把事情分为好几层,一层一层地去做。高层的工作总是建立在低层的工作之上。层次关系主要有两种:上下级关系和顺序相邻关系
    1. 上下级关系的层次结构:上层子系统可以使用下层子系统的功能,而下层子系统不能够使用上层子系统的功能
    2. 顺序相邻关系的层次结构:顺序相邻关系的层次结构表明通讯只能在相邻两层之间发生,信息只能被一层一层地顺序传递
    3. 其他层次结构
  1. 客户机/ 服务器结构
    1. 以集中的方式高效率地管理通讯。前面讲电话系统的故事就是要说明这一点。
    2. 可以共享资源。比如在信息管理系统中,服务器将信息集中起来,任何客户机都可以通过访问服务器而获得所需的信息。

模块设计

  1. 评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封闭——开放性
  2. 信息隐藏
    1. 如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可外扬”就是这个道理
    2. 模块的信息隐藏可以通过接口设计来实现。
  1. 内聚耦合【内聚耦合翻译过来就是反连坐制度,一个人出问题不影响周围的邻居】
    1. 内聚强度
      1. 偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。
      2. 逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的修改也比较困难。
      3. 时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但这些功能只是因为时间因素关联在一起,则称为时间内聚。
      4. 过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次序执行,则称为过程内聚。
      5. 通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为通信内聚。
      6. 顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出作为另一个成分的输入,则称为顺序内聚。
      7. 功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。
    1. 耦合强度:依赖于以下几个因素耦合的强度依赖于以下几个因素:一个模块对另一个模块的调用;一个模块向另一个模块传递的数据量;一个模块施加到另一个模块的控制的多少;模块之间接口的复杂程度。耦合按从强到弱的顺序可分为以下几种类型
      1. 内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。
      2. 公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。
      3. 控制耦合。一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。
      4. 标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构的变化将使相关的模块发生变化。
      5. 数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。
      6. 非直接耦合。模块间没有信息传递时,属于非直接耦合。如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,坚决避免使用内容耦合。
  1. 封闭开放性:如果一个模块可以作为一个独立体被其它程序引用,则称模块具有封闭性。如果一个模块可以被扩充,则称模块具有开放性。

数据结构算法设计

  1. 该部分内容想必对于经常刷算法题的同学来说已经烂熟于心,因此不做过多解释
  2. 对于这部分内容不熟练的同学请去刷leetcode官网多刷题

用户界面设计

  1. 这部分内容就是我们平常看到的界面,如果太难看的程序往往我们就没有用下去的想法,正所谓人要衣装,佛要金装,这让笔者想起了学校的教务系统,怎一个烂字了得
  2. 这里其实和程序员的关系联系就稍微少一些了,更多的是美学,因此同学们千万不要沉迷于编程对其他事物充耳不闻,据说爱因斯坦身为科学家对艺术有着很高的研究,怎么样是不是颠覆了同学们的认知,在这里只简单介绍界面美相对有三个方面
    1. 界面核实性
    2. 界面的风格
    3. 界面的广义美

编码与测试

  1. 这里就来到了所有开发者最讨厌的地方,因为这个组别的同学总能给你挑出刺来,这让笔者想起了大学做课设的时候有一个划水的舍友,啥都没干,就去做测试,结果偏偏同样的数据经过笔者的手没问题,经过他的手就会爆炸,真是想把他千刀万剐
  2. 测试的目的:尽可能发现多的缺陷,言下之意一个挑不出缺陷的程序反而是有问题的,也永远不可能做出没有缺陷的程序,因此开发和测试总是相爱相杀【听说测试组女生偏多,因为程序员社交圈小,所以开发和测试跑多了往往最后结婚了,还好笔者在大学就早早破圈了,认识不少文科妹子】
  3. 测试有助于提高软件的质量,但是提高软件的质量不能依赖于测试,就好像我们是先会做题再检查,但是白卷怎么检查呢,软件的高质量是设计出来的,而不是靠测试修补出来的

维护

维护的尝试

  1. 任何东西用多了总会磨损,所以为了减少这方面的工作,同学们所做的程序要尽可能耐操,要沙漠能用,沼泽能用,太空能用,热带能用,寒带能用【开个玩笑哈】
  2. 一些学者将软件维护划分为主要的三类
    1. 纠错性维护。由于前期的测试不可能揭露软件系统中所有替在的错误,用户在使用软件时仍将会遇到错误,诊断和改正这些错误的过程称为纠错性维护。
    2. 适应性维护。由于新的硬件设备不断推出,操作系统和编译系统也不断地升级,为了使软件能适应新的环境而引起的程序修改和扩充活动称为适应性维护。
    3. 完善性维护。在软件的正常使用过程中,用户还会不断提出新的需求。为了满足用户新的需求而增加软件功能的活动称为完善性维护。

维护的代价及其主要因素

  1. 非技术因素
    1. 应用域的复杂性。如果应用域问题已被很好地理解,需求分析工作比较完善,那么维护代价就较低。反之维护代价就较高。
    2. 开发人员的稳定性。如果某些程序的开发者还在,让他们对自己的程序进行维护,那么代价就较低。如果原来的开发者已经不在,只好让新手来维护陌生的程序,那么代价就较高。
    3. 软件的生命期。越是早期的程序越难维护,你很难想像十年前的程序是多么的落后(设计思想与开发工具都落后)。一般地,软件的生命期越长,维护代价就越高。生命期越短,维护代价就越低。
    4. 商业操作模式变化对软件的影响。比如财务软件,对财务制度的变化很敏感。财务制度一变动,财务软件就必须修改。一般地,商业操作模式变化越频繁,相应软件的维护代价就越高。
  1. 技术因素
    1. 软件对运行环境的依赖性。由于硬件以及操作系统更新很快,使得对运行环境依赖性很强的应用软件也要不停地更新,维护代价就高。
    2. 编程语言。虽然低级语言比高级语言具有更好的运行速度,但是低级语言比高级语言难以理解。用高级语言编写的程序比用低级语言编写的程序的维护代价要低得多(并且生产率高得多)。一般地,商业应用软件大多采用高级语言。比如,开发一套Windows 环境下的信息管理系统,用户大多采用 Visual Basic、Delphi 或 Power Builder来编程,用 Visual C++的就少些,没有人会采用汇编语言。
    3. 编程风格。良好的编程风格意味着良好的可理解性,可以降低维护的代价。
    4. 测试与改错工作。如果测试与改错工作做得好,后期的维护代价就能降低。反之维护代价就升高。
    5. 文档的质量。清晰、正确和完备的文档能降低维护的代价。低质量的文档将增加维护的代价(错误百出的文档还不如没有文档)。

相关