高软学习总结

文章介绍了软件工程的基础知识,包括VSCode编辑器的特性,Git的版本控制,vim的文本编辑,正则表达式的使用。此外,还探讨了代码规范和高质量代码的编写,模块化设计,以及从需求分析到软件设计的过程,如用例建模和统一过程。文章还提到了软件设计模式和软件架构的重要性。
摘要由CSDN通过智能技术生成

总体概述

经过一个学期的学习,我对于软件工程有了更深的理解。因为我是一个跨考生,所以其实很多基本概念了解的不是特别多,老师从基础的一些工具和一些现实中发生的一些软件案例出发为我们讲解,这对于我们来说正好可以接受,也在这个过程中慢慢学到了很多有用的知识,下面是本课程的整体回顾

一、工欲善其事必先利其器

这部分主要介绍一些工具的用法及软件设计中的一些理念

1.1 VS Code

VS Code近年来获得了爆炸式增长,成为广大开发者工具库中的必备神器。其成功之处主要在于设计者简洁而聚焦的产品定位、进程隔离的插件模型、LSP和DAP两大协议

  • VS Code专注于开发者“最常用”的功能:编辑器+代码理解+版本控制+远程开发+调试。这是一个非常节制而平衡的选择,专注于开发者“最常用”的功能,同时在产品的形式上力求简洁高效。从结果来看,这个定位是相当成功的。

  • 稳定性对于 VS Code 来说尤为重要。都知道 VS Code 基于 Electron.js,实质上是个 Node.js 环境,Node.js是单线程的,任何代码崩了都是灾难性的后果。所以 VS Code 干脆不信任任何人,把插件们放到单独的进程里,插件进程怎么折腾也无法干扰主进程代码的执行,主程序的稳定性得到了保障

  • 代码理解和调试,绝大部分功能都是由第三方插件来实现的,这些用于代码理解和调试的第三方插件与VS Code主进程之间的桥梁就是两大协议——Language Server Protocol(LSP)和 Debug Adapter Protocol(DAP)。

    • LSP 显然把小巧作为设计目标之一,它选择做最小子集,贯彻了团队一贯节制的作风。它关心的是用户在编辑代码时最经常处理的物理实体(比如文件、目录)和状态(光标位置)。它根本没有试图去理解语言的特性,编译也不是它所关心的问题,所以自然不会涉及语法树一类的复杂概念。
    • 小归小,功能可不能少,所以抽象就非常关键了。LSP 最重要的概念是动作和位置,LSP 的大部分请求都是在表达”在指定位置执行规定动作“。
    • 其次这是一个基于 JSON 的协议,JSON 可以说是最易读的结构化数据格式了。

另外还涉及到一些常用快捷键的用法:在这里插入图片描述

1.2 Git

分布式版本控制系统,比如Git,是目前世界上最先进的分布式版本控制系统(没有之一)。
我觉得最有用的可能就是团队开发的整个流程,如下所示:

# 克隆或同步最新的代码到本地存储库
git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git
git pull 
# 为自己的工作创建一个分支,该分支应该只负责单一功能模块或代码模块的版本控制
git checkout -b mybranch
git branch
# 在该分支上完成某单一功能模块或代码模块的开发工作;多次进行如下操作:
git add FILES
git commit -m "commit log"
# 最后,先切换回master分支,将远程origin/master同步最新到本地存储库,再合并mybranch到master分支,推送到远程origin/master之后即完成了一项开发工作。
git checkout master
git pull
git merge --no-ff mybranch
git push

当然如果是你在github上要参与别人的项目,就需要遵循fork+pull request的规则,即先fork别人的仓库,自己修改之后提交给别人审查之后才合并。

1.3 vim

vim是从vi发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。
各种模式之间的切换:
在这里插入图片描述

1.4 Regular Expression

正则表达式的应用范围非常之广泛,最初是由Unix普及开来的,后来在广泛运用于Scala 、PHP、C# 、Java、C++ 、Objective-c、Perl 、Swift、VBScript 、Javascript、Ruby 以及Python等等。
介绍一些常用的通配符及一些用法:

  • 通配符“.”将匹配任意一个字符。通配符也可称为 dot 和 period。你可以像正则表达式中的任何其他字符一样使用通配符。例如,如果你想匹配“hug”,“huh”,“hut”和“hum”,可以使用正则表达式hu.来匹配这所有四个字符串。
  • 通配符“+”用来查找出现一次或多次的字符,例如hahhhhh,可以使用正则表达式hah+来匹配。
  • 通配符“*”匹配零次或多次出现的字符,使用正则表达式hah*来匹配,还可以匹配ha字符串。
  • 通配符“?”指定可能存在的元素,也就是检查前一个元素存在与否,如正则表达式colou?r、favou?rite中通配符“?”前面的u字符存在和不存在两种情况的字符串都会匹配。
  • 数量说明符使用大括号{and}。你将两个数字放在大括号之间用逗号“,”隔开表示上限和下限数。要匹配字符串"aaah"中出现 3 到 5 次的 a,你的正则表达式将是a{3,5}h;
  • 用方括号[and]中来定义一组你希望匹配的字符。例如,你要匹配"bag",“big"和"bug”,你可以创建正则表达式/b[aiu]g 来执行此操作。
  • 插入字符“^”用于表示字符串的开头。美元字符“$”表示字符串的末尾。例如在“Ricky is first and can be found”中查找开头的“Ricky”则为^Ricky,查找结尾的“found”则为found$。
  • 使用 capture groups 捕获组来匹配字符串中连续出现三次的数字,每个数字由空格分隔,如(\d+)\s\1\s\1。

二、代码中的软件工程-工程化编程实战

2.1 代码规范和代码风格

一个项目代码的风格就如同一个人给人的印象,代码风格之所以那么重要,是因为它往往决定了代码是否规范、是否易于阅读。
我们把代码的风格分成三重境界:

  • 一是规范整洁。遵守常规语言规范,合理使用空格、空行、缩进、注释等;
  • 二是逻辑清晰。没有代码冗余、重复,让人清晰明了的命名规则。做到逻辑清晰不仅要求程序员的编程能力,更重要的是提高设计能力,选用合适的设计模式、软件架构风格可以有效改善代码的逻辑结构,会让代码简洁清晰;
  • 三是优雅。优雅的代码是设计的艺术,是编码的艺术,是编程的最高追求。
  • 一般来讲,我们对代码风格的基本原则要求是简明、易读、无二义性。

代码风格规范总结:

  • 缩进:4个空格;行宽:< 100个字符;
  • 代码行内要适当多留空格,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d));
  • 在一个函数体内,逻揖上密切相关的语句之间不加空行,逻辑上不相关的代码块之间要适当留有空行以示区隔;
  • 在复杂的表达式中要用括号来清楚的表示逻辑优先级;
  • 花括号:所有 ‘{’ 和 ‘}’ 应独占一行且成对对齐;不要把多条语句和多个变量的定义放在同一行;

2.2 编写高质量代码的方法

  • 通过控制结构简化代码(if else/ switch/ while)
  • 通过数据结构简化代码
  • 一定要有错误处理
  • 注意性能优先的代价
  • 拒绝修修补补,要不断重构代码

2.3 模块化软件设计

模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns)。
每个模块只有一个功能,易于开发,并且bug会集中在少数几个模块内,容易定位软件缺陷,也更加容易维护。
软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。一般在软件设计中我们追求松散耦合。
在这里插入图片描述
内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。
理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性(Feather)。

基本方法

  • KISS(keep it simple&stupid)原则
    • 一行代码只做一件事
    • 一个块代码只做一件事
    • 一个函数只做一件事
    • 一个软件模块只做一件事
  • 使用本地化外部接口,本质上是一种设计模式,代理模式,即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。能够有效降低模块与外部的耦合度。
  • 先写伪代码

2.4 可重用软件设计-接口

接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。
在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;而在面向对象的编程中,接口是对象对外开放(public)的一组属性和方法的集合。函数或方法具体包括名称、参数和返回值等。
接口规格包含五个基本要素:

  • 接口的目的
  • 接口的前置条件
  • 接口的协议规范(如http协议,png图片格式,json数据格式定义etc…)
  • 接口的后置条件
  • 接口的质量属性(如响应时间)

RESTful API:GET用来获取资源;POST用来新建资源(也可以用于更新资源);PUT用来更新资源;DELETE用来删除资源。
常见的耦合方式有三种:

  • 公共耦合: 当软件模块之间 共享数据区或 变量名的软件模块之间即是公共耦合,显然两个软件模块之间的接口定义不是通过显式的调用方式,而是隐式的共享了共享了数据区或变量名。
  • 数据耦合:在软件模块之间仅通过显式的调用传递基本数据类型即为数据耦合。
  • 标记耦合:在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合没有经过显式的调用传递数据的方式耦合度要低。

通用接口定义的基本方法:

  • 参数化上下文(使用参数传递信息,不依赖上下文环境,即不使用闭包函数)
  • 移除前置条件(sum函数中使用数组传递参数,不再限定参数个数)
  • 简化后置条件(移除参数之间的关系,使sum返回的是数组全部元素的和)

三、从需求分析到软件设计

3.1 获取需求的主要方法

需求分析就是需求分析师对用户期望的软件行为进行表述,并进一步用对象或实体的状态、属性和行为来定义需求。
需求的类型划分如下:

  • 功能需求:根据所需的活动描述所需的行为
  • 质量需求或非功能需求:描述软件必须具备的一些质量特性
  • 设计约束: 设计决策,例如选择平台或接口组件
  • 过程约束: 对可用于构建系统的技术或资源的限制

高质量需求的特点:

  • Making Requirements Testable(需求可测试)
  • Resolving Conflicts(解决冲突)
  • Characteristics of Requirements(需求很有特点)

3.2 对需求进行分析和建模

需求分析的两种基本方法:

  • 原型化方法:原型化方法可以很好地整理出用户接口方式(UI,User Interface),比如界面布局和交互操作过程。
  • 建模的方法:建模的方法可以快速给出有关事件发生顺序或活动同步约束的问题,能够在逻辑上形成模型来整顿繁杂的需求细节。

用例

基本概念:用例(Use Case)的核心概念中首先它是一个业务过程(businessprocess),经过逻辑整理抽象出来的一个业务过程,这是用例的实质。什么是业务过程?在待开发软件所处的业务领域内完成特定业务任务(business task)的一系列活动就是业务过程。
四个必要条件:

  • 必要条件一 :它是不是一个业务过程?
  • 必要条件二:它是不是由某个参与者触发开始?
  • 必要条件三:它是不是显式地或隐式地终止于某个参与者?
  • 必要条件四: 它是不是为某个参与者完成了有用的业务工作?

三个抽象层级

  • 抽象用例(Abstract use case)。只要用一个干什么、做什么或完成什么业务任务的动名词短语,就可以非常精简地指明一个用例。
  • 高层用例(High level use case)。需要给用例的范围划定一个边界,也就是用例在什么时候什么地方开始,以及在什么时候什么地方结束;
  • 扩展用例(Expanded use case)。需要将参与者和待开发软件系统为了完成用例所规定的业务任务的交互过程一步一步详细地描述出来,一般我们使用一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来。扩展用例最后可以用两列表格描述。

用例建模过程:
在这里插入图片描述

3.3 从需求分析到软件设计

我们所遵循的软件过程——敏捷统一过程。为了理解敏捷统一过程,我们先从瀑布模型(Waterfall Process)说起。
瀑布模型是最基本的过程模型,它把整个软件过程按顺序划分成了需求、设计、编码、测试和部署五个阶段。瀑布模型的根本特点是按顺序划分阶段。

统一过程

统一过程(UP,Unified Process)的核心要义是用例驱动(Use case driven)、以架构为中心(Architecture centric)、增量且迭代(Incremental and Iterative)的过程。用例驱动就是我们前文中用例建模得到的用例作为驱动软件开发的目标;以架构为中心的架构是后续软件设计的结果,就是保持软件架构相对稳定,减小软件架构层面的重构造成的混乱。
敏捷统一过程的四个关键步骤:

  • 第一,确定需求;
  • 第二,通过用例的方式来满足这些需求;
  • 第三,分配这些用例到各增量阶段;
  • 第四,具体完成各增量阶段所计划的任务。

显然,第一到第三步主要是计划阶段的工作,第四步是接下来要进一步详述的增量阶段的工作。
在每一次增量阶段的迭代过程中,都要进行从需求分析到软件设计实现的过程,具体敏捷统一过程
将增量阶段分为五个步骤:

  • 用例建模(Use case modeling);
  • 业务领域建模(Domain modeling);
  • 对象交互建模(Object Interaction modeling),使用剧情描述来建模,最后转换为剧情描述表;
  • 形成设计类图(design class diagram);
  • 软件的编码实现和软件应用部署;

形成设计类图的主要步骤:

  • 识别每个序列图中使用的所有类并将它们放入 DCD(设计类图) 中
  • 识别属于每个类的方法并将它们放入到 DCD 中
  • 识别并填写序列图和域模型中的属性
  • 识别并填写序列图和领域模型中的关系

四、软件科学基础概论

4.1 软件是什么

对象组合优于继承:基于对象(类)之间的继承关系所形成的两个对象各自独立、两个类紧密耦合的程序结构;对象组合则将一个对象作为另一个对象的属性,从而形成两个对象(类)之间有明确的依赖关系。继承可以重用代码,但是破坏了代码的封装特性,增加了父类与子类之间的代码模块耦合,因此我们需要避免使用继承,在大多数情况下,应该使用对象组合替代继承来实现相同的目标。
具体的优点:

  • 避免继承的紧耦合:继承是一种紧耦合的关系,子类与父类之间存在强依赖关系。当父类发生改变时,子类也会受到影响,需要相应地进行修改。这种紧耦合限制了代码的灵活性和可维护性。而对象组合则是一种松耦合的关系,对象之间通过组合关系进行连接,各个对象之间的修改相互独立,一个对象的变化不会对其他对象造成影响,因此更加灵活和易于维护。
  • 提供更大的灵活性:对象组合允许在运行时动态地改变对象之间的关系,而继承的关系在编译时就已经确定了。通过对象组合,可以根据需要组合不同的对象来实现不同的功能,而无需事先定义固定的类层次结构。这种灵活性使得代码更加可扩展和可定制。
  • 可以避免类爆炸问题:当需要实现多个不同的功能组合时,使用继承会导致类的爆炸,需要定义大量的子类。而对象组合可以通过组合不同的对象来实现不同的功能,避免了类爆炸问题,减少了代码的复杂性。
  • 更好地符合单一职责原则:继承往往将多个不同的功能集成到一个类中,导致类的职责不够单一。而对象组合将不同的功能封装到独立的对象中,每个对象只负责自己的职责,更符合单一职责原则,使得代码更加清晰和易于理解。

继承复用的缺点:在这里插入图片描述
合成复用的优点:在这里插入图片描述

4.2 软件设计模式

先来看看设计模式要解决的问题。功能分解是处理复杂问题的一种自然的方法,但是需求总是在发生变化,功能分解不能帮助我们为未来可能的变化做准备,也不能帮助我们的代码优雅地演化。结果你想在代码中做一些改变,但又不敢这么做,因为你知道对一个地方代码的修改可能在另一个地方造成破坏。
设计模式的七大原则:

  • 单一职责原则 (Single Responsibility Principle)
  • 开放关闭原则 (OpenClosed Principle)
  • 里氏替换原则 (Liskov Substitution Principle)
  • 依赖倒转原则 (Dependence Inversion Principle)
    • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。由于在软件设计中,细节具有多变性,而抽象层则相对稳定,因此以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多。依赖倒置原则在模块化设计中降低模块之间的耦合度和加强模块的抽象封装提高模块的内聚度上具有普遍的指导意义
  • 接口隔离原则 (Interface Segregation Principle)不考
  • 迪米特法则(Law Of Demeter)
  • 组合/聚合复用原则 (Composite/Aggregate Reuse Principle)

4.3 常见的软件架构

mvc和mvvm:主要区别在于,MVC中,用户对于M的操作是通过C传递的,然后C将改变传给V,并且M将在发生变化时通知V,然后V通过C获取变化;在MVVM中,用户直接与V交互,通过VM将变化传递给M,然后M改变之后通过VM将数据传递给V,从而实现解耦。另一个区别是,当M的数据需要进行解析后V才能使用时,C若承担解析的任务,就会变得很臃肿;在MVVM中,VM层承担了数据解析的工作,这时C就只需要持有VM,而不需要直接持有M了。从而完成了数据的解耦。
软件架构复用:

  • 克隆,完整地借鉴相似项目的设计方案,甚至代码,只需要完成一些细枝末节处的修改适配工作。
  • 重构,构建软件架构模型的基本方法,通过指引我们如何进行系统分解,并在参考已有的软件架构模型的基础上逐步形成系统软件架构的一种基本建模方法。

五、软件危机和软件过程

5.1 软件危机

没有银弹:“在10年内无法找到解决软件危机的杀手锏(银弹)。
软件中的根本困难,即软件概念结构(conceptual structure)的复杂性,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困境。

5.2 软件过程模型

每一个软件的生命周期都是独一无二的,但是从众多独一无二的软件个体中却呈现出来一些固有的模式,类似于软件设计模式、建筑设计模式,乃至人生模式,软件生命周期的模式我们称之为软件过程模型。
软件生命周期分为分析、设计、实现、交付和维护五个阶段:

  • 分析阶段的任务是需求分析和定义,分析阶段一般会在深入理解业务的情况下,形成业务概念原型
  • 设计阶段分为软件架构设计和软件详细设计,前者一般和分析阶段联系紧密,一般合称为“分析与设计”;后者一般和实现阶段联系紧密,一般合称为“设计与实现”。
  • 实现阶段分为编码和测试,其中测试又涉及到单元测试、集成测试、系统测试等。
  • 交付阶段主要是部署、交付测试和用户培训等。
  • 维护阶段一般是软件生命周期中持续时间最长的一个阶段,而且在维护阶段很可能会形成单独的项目,从而经历分析、设计、实现、交付几个阶段,最终又合并进维护阶段。

软件过程又分为描述性的(descriptive)过程和说明性的(prescriptive)过程。

  • 描述性的过程试图客观陈述在软件开发过程中实际发生什么。
  • 说明性的过程试图主观陈述在软件开发过程中应该会发生什么。

采用不同的过程模型时应该能反映出要达到的过程目标,比如构建高质量软件、早发现缺陷、满足预算和日程约束等。不同的模型适用于不同的情况,我们常见的过程模型,比如瀑布模型、V模型、原型化模型等都有它们所能达到的过程目标和适用的情况。

作者:337
参考资料:《代码中的软件工程》https://gitee.com/mengning997/se

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值