Loading

代码中的软件工程复习

编写高质量代码的基本方法

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

性能优先策略带来的隐藏代价

  • 软件工程师的人力成本远大于所消耗的计算资源成本时,提高代码编写的工作效率将更有价值;
  • 质量保证的人力成本和质量保证的成效也比所消耗的计算资源成本更有价值;
  • 性能优先的策略往往会让代码很难理解,结果需要消耗更多的工时;
  • 面向机器的代码修改起来更困难,可扩展性差,同样会消耗更多工时。

模块化的基本原理

模块化是在软件系统设计时保持系统内各部分相对独立,以便每一部分可以独立的设计和开发。基本原理是关注点的分离。通过“分而治之”,将复杂问题分解成一个个简单问题,减少出错的情形。

我们一般使用耦合度和内聚度来衡量软件模块化的程度。内聚度是一个软件模块内部各个元素的紧密程度,耦合度是软件模块之间依赖程度。

为什么追求高内聚低耦合?

内聚度越高,模块内只做一件事,只完成一个主要的功能点。这使得每一个模块都易于开发和理解。

在软件设计中,我们更追求松散耦合。这样,整个系统更容易定位软件bug,因为每一个bug都局限在一两个模块内。整个系统也更好维护,扩展时只需要变更少量的模块。

软件设计中的一些基本方法

KISS原则:一行代码只做一件事,一个块代码只做一件事,一个函数只做一件事,一个软件模块只做一件事。

使用本地化外部接口来提高代码的适应能力,即将外部代码进行二次封装,写成本地接口,能更好的帮助我们分离业务之间的关联性,降低模之间的耦合度,使得代码开发更加高效。——“不要和陌生人说话”

先使用伪代码写出代码结构,一是可以提供一个框架,避免代码无序生长;二是不需要考虑异常处理等变成细节,避免了编码带来的结构性损失。

消费者重用

软件开发者在项目中重用已有的软件模块。一般要重点考虑以下四点:

  • 该模块是否能满足项目所要求的功能
  • 重构该模块是否比从头构建一个软件模块工作量少
  • 该模块是否有完整的文档说明
  • 该模块是否有完整的测试和修订记录

生产者重用

生产者重用即生产可以重用的软件模块。一般要考虑以下几点:

  • 通用模块才有重用的机会
  • 对设计的接口要有完善的描述
  • 进行测试并且有修订记录
  • 一致的命名规则
  • 对用到的数据结构要有完整的说明

接口的基本概念

接口是互相联系的双方共同遵守的一种协议规范。

在软件模块内部,一般是通过一组函数API来约定软件模块间的沟通方式。

对于面向过程编程,是通过一些数据结构和操作这些数据结构的方法来定义接口。对于面向对象的编程,一般是用对象对外暴露的属性和方法来定义接口。

接口的五个基本要素

接口的目的

接口的前置条件(如参数在什么时候才有意义)

接口双方遵守的协议规范(如互相联系双方的传入参数都必须是某种数据结构)

接口的后置条件(如 返回值)

接口所隐含的质量属性

为什么使用微服务而不是单体集中式架构

通过模块化的思想垂直划分业务功能,由一系列微服务共同组成软件系统的一种架构模式。

每个微服务独立部署,跑在自己的进程中,有自己独立的堆栈。每一个微服务可以分解成最小的产品,达到功能内聚。微服务之间无耦合或者有极为松散的耦合,系统通过前端应用来聚合微服务达到业务功能。

微服务架构的出现是由于传统的单体服务器转变为基于虚拟化技术和分布式云计算技术的PC服务器的大规模集群。

接口与耦合度之间的关系

公共耦合:即两个软件模块之间的接口定义不是通过显式调用,而是通过隐式共享数据区或变量名。

数据耦合:仅通过显式调用传递基本数据类型。

标记耦合:通过显式调用结构化数据,此时数据的结构称为软件模块之间的隐含规定,因此耦合度比数据耦合高。但比公共耦合这种没有显式调用的数据传递方式要低。

通用接口定义的基本方法

参数化上下文

移除前置条件

简化后置条件

可重入函数

可重入函数即可以又多个并发任务使用,而又不用担心错误。可重入函数只使用自己栈帧的数据,可以在任何时候中断。

如果要使用全局变量或者static,一定要注意保护自己的数据(如关中断、信号量)。可重入函数不能调用malloc和free这样的不可重入函数,不返回指向静态数据的指针。

什么是线程安全

如果所在的进程中有多个线程,当且仅当多个并发线程反复调用这段代码且结果总是正确,就说这段代码是线程安全的。

线程安全问题是由于全局变量和静态变量引起的。如果只对全局变量或者static变量进行读操作,一般来说是安全的,但是如果多个线程执行写操作,就需要考虑同步和互斥的问题。

可重入函数是线程安全函数的子集,因此可重入函数一定是线程安全的。而线程安全不一定是可重入的。

RESTful API

表现层状态转化,有表现层一定有背后的信息实体,信息实体就是URI代表的资源,状态转换是通过HTTP里定义的四种操作方式:

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源

为什么要有分支合并

每一个版本都是上一个版本的增量补丁,将要合并的分支里的BDF几个增量补丁合并到当前的工作区,解决冲突后提交为版本H,这就是合并。

image-20220106192301979

如果多人都同时向远程master提交代码,一是可能会有冲突,二是git log排列在一条时间线上,不利于查看或者回退代码。 可以使用git merge --no-ff,让一段连续的工作在commit日志时间线呈现出一条条支线。

git merge和git merge --no-ff的区别

git merge –-no-ff 可以保存你之前的分支历史。能够更好的查看 merge历史,以及branch 状态。

git merge 则不会显示 feature,只保留单条分支记录。

image-20220106193826001

团队项目的开发者的工作流程

  1. 克隆或同步最新的代码到本地存储库(git clone ——克隆远程库到当前目录下/ git pull——从其他库或分支抓取并合并到当前分支)
  2. 为自己创建一个分支,只负责单一模块的版本控制(git checkout -b mybranch——创建新分支/git branch ——切换分支)
  3. 在该分支上完成代码开发工作,多次进行如下操作(git add FILE——把文件添加到暂存区/ git commit -m "log"把暂存区文件提交到仓库)
  4. 先切换回master(git checkout master),再将远程master同步拉取到本地存储库(git pull),再合并分支到master(git merge --no-ff mybranch),最后推送到远程master(git push)。

git rebase

可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase命令可以使我们的提交历史干净、简洁!但是通过rebase对任何已经提交到公共仓库的commit进行修改!

vim命令和通配符

u复原前一个命令

ctrl r重做上一个动作

“*”表示前一个字符出现 0 次、1 次或多次;“+”表示前一个字符出现一次或多次

posted @ 2022-01-07 20:48  乌有先生ii  阅读(107)  评论(0编辑  收藏  举报