Linux调试私房菜(九)设计,软件质量之本

2023-05-16

十八、设计,软件质量之本(上)

1. 软件设计是什么?

  • 一种创造性活动,力求以简单优雅的方式解决实际问题
    • 软件设计是一门技术
      • 数据结构,组成原理,操作系统,编程语言,。。。
    • 软件设计是一门艺术
      • 并不是技术知识的简单堆砌
      • 分析,抽象,取舍,。。。

2. 软件设计的意义(架构的意义)

3. 软件设计的特点

  • 软件设计是一个塑造模型(概念)的过程
  • 软件设计是一个取舍的过程
  • 软件设计是一个分而治之的过程
  • 软件设计是一个在理性范围内追求完美的过程

4. 什么是软件质量?

  • 用户角度
    • defect 和 bug 的数量越少意味着质量越好
  • 开发角度
    • 整体架构设计易于扩展
    • 模块之间耦合性低,易于复用
    • 代码简洁易懂,易于维护

5. 实例分析:质量对团队的影响

6. 如何提高软件质量?

  • 编码前,架构设计质量必须得到保证
  • 编码时,代码质量必须得到保证
  • 编码后,测试质量必须得到保证

7. 架构设计 VS 软件质量

8. 开发流程 VS 软件质量

9. 软件测试 VS 软件质量

10. 为测试正名

  • 在需求分析结束后,测试人员就需要介入项目
    • 根据需求分析进行功能测试用例的设计
    • 根据架构设计进行模块测试用例的设计
    • 根据产品标准进行压力测试用例的设计

11. 开发中的常见问题

  • 测试是替罪羊或救命稻草
    • 但凡出现bug,就是测试不给力
  • 资源永远不足
    • 现在没有时间和精力去做重构
  • 不改变就可以规避风险
    • 虽然有缺陷,但是功能不受影响,不做改变

12. 如何提高自身的软件设计能力?

  • 对架构的完美性有精神上的追求,不满足功能正确
    • 积极思考方案,不停反思是否能做得更好
  • 勇于实践与模仿,进而形成自己的风格和思想
    • 推敲前辈们的经典设计,尝试用于自己的项目
  • 总结设计原则,体会各个设计原则的内涵
    • 软件设计不是原则的叠加,而是一个平衡利弊的过程

13. 设计思想,设计原则,设计模式

14. 小结

  • 软件设计(架构)的质量决定了软件产品质量的基调
  • 测试质量代码质量直接夫定最产品的质量
  • 正规开发流程中测试人员先于编码人员介入项目
  • 软件架构设计能力需要通过实践提高,需要总结和体会

十九、设计,软件质量之本(中)

1. 设计原则 - 1:以人为本

  • 核心:将现实世界直接映射到软件世界
  • 意图:便于沟通和理解,降低复杂性,增加维护性
  • 要点:使用现实世界中的概念

2. 案例:任务与内存访问

3. 架构设计图

4. 设计原则 - 2:简单即是美

  • 核心:用最简单的方法描述解决方案
  • 意图:便于沟通和理解,降低复杂性,增加维护性
  • 要点:使用团队熟悉的技术进行设计

5. 案例:删除格式化字符串中的指定子串

   方案2相对最好。

6. 简单性 VS 灵活性

  • 简单性不等于灵活性
  • 简单的设计易于催生灵活的设计
  • 过于追求灵活的设计可能导致复杂性的增加

7. 设计原则 - 3:让模块善始善终

  • 核心:模块的初始化与模块的终止同等重要
  • 意图:确保模块状态的恢复和保存
  • 要点:对称式设计

8. 案例:模块的动态加载(初始化)与卸载(终止)

9. 设计方案

10. 设计原则 - 4:重视运行时数据的收集

  • 核心:考虑程序运行状态数据的收集模块
  • 意图:监控程序运行状态,便于调式与测试

11. 案例:统一日志模块的设计

12. 案例:程序状态统计模块的设计

13. 小结

  • 架构设计时尽量将现实中的概念映射到程序中
  • 开发过程中的任意阶段都提倡简单优美的设计方式
  • 模块设计时需要同时考虑初始化过程与终止过程
  • 架构设计时需要考虑保证产品质量的辅助手段

二十、设计,软件质量之本(下)

1. 设计原则 - 5:代码自注释

  • 核心:代码自身就能够很好的进行功能性说明
  • 意图:便于沟通和理解,增加维护性

2. 案例分析

3. 代码质量

  • 最终的产品代码应该 “非常容易” 读懂
  • 注释作为补充说明必不可少,但不是越多越好

注释应该起到画龙点睛的作用,用于简要的描述代码意图;避免使用注释描述程序的运行流程。

4. 设计原则 - 6:通过机制解决问题

  • 核心:考虑当前设计是否存在 漏洞
  • 意图:杜绝类似问题的再次发生

5. 案例:消息传递

6. 问题

  • 当A设备无法收到B设备的RSP消息时,会发生什么?
    • 相互等待

7. 设备状态

8. 解决方案一

让设备B定时重发RSP消息

9. 解决方案二

  • 加消息中转层
    • 功能定义:
      • 负责所有设备间通信消息的发送和接收
      • 处理所有通信异常的处理(阻塞重发,报告错误,etc.

10. 正常消息交互

11. 异常消息交互

12. 架构经验

  • 设计不是一次性完成的,需要根据实际问题进行重构。

13. 设计原则 - 7:防御性程序设计

  • 核心:防止他人的“意外”错误
  • 意图:提高代码鲁棒性

14. 案例:定时器模块的设计与实现

  • 创建定时器后每隔指定的时间能够触发事件
  • 事件的具体表现为关联的回调函数被调用

15. 设计草图

16. 实例分析定时器的设计与实现


#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "timer.h"

#define MAX 32
#define GAP 10

struct STimer
{
    int id;
    int interval;
    int current;
    int in_callback;
    int to_delete;
    TimerCallback* callback;
    void* data;
};

static struct STimer* g_timers[MAX];
static volatile int g_run;
static pthread_mutex_t mutex;

static void* Runtime(void* args)
{
    int i = 0;
    
    while( g_run )
    {     
        pthread_mutex_lock(&mutex); 
        
        for(i=0; (i<MAX) && g_run; i++)
        {
            struct STimer* st = g_timers[i];
            
            if( (st != NULL) && (st->callback != NULL) )
            {
                st->current += GAP;
            
                if( st->current >= st->interval )
                {
                    st->in_callback = 1;
                    st->callback(st, st->data);
                    st->in_callback = 0;
                    
                    st->current = 0;
                    
                    if( st->to_delete )
                    {
                        g_timers[st->id] = NULL;
            
                        free(st);
                    }
                }
            }
        }
        
        pthread_mutex_unlock(&mutex);  
        
        usleep(GAP * 1000);
    }
    
    for(i=0; i<MAX; i++)
    {
        free(g_timers[i]);
    }
}


void TimerInitialize()
{
    if( !g_run )
    {
        pthread_t tid = 0;
        
        g_run = 1;

        pthread_create(&tid, NULL, Runtime, NULL);
    }
}

void TimerFinalize()
{
    g_run = 0;
}

Timer* CreateTimer(int interval, TimerCallback* callback, void* data)
{
    int id = 0;
    struct STimer* ret = (struct STimer*)malloc(sizeof(struct STimer));
    
    if( ret != NULL )
    { 
        pthread_mutex_lock(&mutex);   
        
        for(id=0; id<MAX; id++)
        {
            if( g_timers[id] == NULL )
            {
                g_timers[id] = ret;
                break;
            }
        }
        
        pthread_mutex_unlock(&mutex);
        
        if( id < MAX )
        {
            ret->id = id;
            ret->interval = interval;
            ret->current = 0;
            ret->to_delete = 0;
            ret->in_callback = 0;
            ret->callback = callback;
            ret->data = data;
        }
        else
        {
            free(ret);
            ret = NULL;
        }
    }
    
    return ret;
}

void DestroyTimer(Timer* timer)
{
    if( timer != NULL )
    {
        struct STimer* st = (struct STimer*)timer;
        
        if( st->in_callback )
        {
            st->to_delete = 1;
        }
        else
        {
            pthread_mutex_lock(&mutex);   
        
            g_timers[st->id] = NULL;
            
            pthread_mutex_unlock(&mutex);   
            
            free(st);
        }
    }
}


#ifndef _TIMER_H_
#define _TIMER_H_

typedef void Timer;
typedef void(TimerCallback)(Timer*, void*);

void TimerInitialize();
void TimerFinalize();
Timer* CreateTimer(int interval, TimerCallback* callback, void* data);
void DestroyTimer(Timer* timer);

#endif

17. 小结

  • 尽量使用代码自注释的方式编写代码,便于沟通维护
  • 注释作为补充说明必不可少,但不是越多越好
  • 思考bug是否因为设计不当造成,通过机制解决问题
  • 通过防御性程序设计提高代码鲁棒性
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux调试私房菜(九)设计,软件质量之本 的相关文章

  • 在内核代码中查找函数的最佳方法[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我开始浏览内核代码 遇到的一件事是如何跟踪函数调用 结构定义等 有没有一种好的方法可以快速跳转到函数定义并退出 我尝试过 Source N
  • SSH,运行进程然后忽略输出

    我有一个命令可以使用 SSH 并在 SSH 后运行脚本 该脚本运行一个二进制文件 脚本完成后 我可以输入任意键 本地终端将恢复到正常状态 但是 由于该进程仍在我通过 SSH 连接的计算机中运行 因此任何时候它都会登录到stdout我在本地终
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • 我们真的应该使用 Chef 来管理 sudoers 文件吗?

    这是我的问题 我担心如果 Chef 破坏了 sudoers 文件中的某些内容 可能是 Chef 用户错误地使用了说明书 那么服务器将完全无法访问 我讨厌我们完全失去客户的生产服务器 因为我们弄乱了 sudoers 文件并且无法再通过 ssh
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • GMail 421 4.7.0 稍后重试,关闭连接

    我试图找出为什么它无法使用 GMail 从我的服务器发送邮件 为此 我使用 SwiftMailer 但我可以将问题包含在以下独立代码中
  • 使用 MAX_ORDER / 包含 mmzone.h

    根据https www kernel org doc Documentation networking packet mmap txt https www kernel org doc Documentation networking pa
  • Bash - 在与当前终端分开的另一个终端中启动命令的新实例

    我有一个简单的 bash 脚本 test sh 设置如下 bin bash args if args 0 check capture then watch n 1 ls lag home user capture0 watch n 1 ls
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • Linux/POSIX:为什么 fork() 不分叉*所有*线程

    众所周知 POSIX下创建新进程的默认方式是使用fork 在 Linux 下 这在内部映射到clone 我想知道的是 众所周知 当一个人打电话时fork 子进程是用单个线程创建的 调用的线程fork cf https linux die n
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • cdc_acm:无法设置 dtr/rts - 无法与 USB cdc 设备通信

    我试图使用 pic24fj128gb206 枚举 usb cdc 设备 设备似乎已正确枚举 但是当我将设备连接到 Linux PC 时 我从内核收到以下警告消息 cdc acm 1 8 1 6 7 1 0 failed to set dtr
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • ansible unarchive 模块如何查找 tar 二进制文件?

    我正在尝试执行一个 ansible 剧本 该剧本的任务是利用unarchive模块 因为我是在 OSX 上执行此操作 所以我需要使用它gnu tar 而不是bsd tar通常与 OSX 一起提供 因为BSD tar 不受官方支持 https

随机推荐

  • 列表拖拽,上下移动 java 接口实现

    列表拖拽实现 上下移动实现 列表拖拽实现 拖拽的逻辑 xff1a 按住某一个id 拖拽到需要移动的某个后面 如 xff1a 1 2 3 4 5 把 1 放到5的后面 列表改变顺序后变成 2 3 4 5 1 所以目前的代码实现逻辑为 把1 挨
  • CentOS8安装Mysql提示Error: Unable to find a match

    yum y install mysql community server Last metadata expiration check 0 13 40 ago on Sun 26 Apr 2020 11 20 57 AM CST No ma
  • 常见协议的默认端口

    常见协议的默认端口 1 HTTP协议 xff0c 服务器的默认端口号为 xff1a 80 2 HTTPS协议 xff0c 服务器默认的端口号为 xff1a 443 3 Telnet协议 xff0c 服务器默认的端口号为 xff1a 23 4
  • 锐捷交换机基本配置命令

    show ip interface brief 查看当前配置的ip地址 enable password admin 给enable配置密码 switchport trunk allowed vlan all 允许全部vlan通过 xff1f
  • 一道经典的数据库sql语句题目

    SQL查询题目 lt 列表视图页显示缩略图和文章摘要 gt lt 文章视图页显示全部内容 gt lt 如果有内容 xff0c 显示内容 gt SQL查询题目 条件 xff1a Student Sno Sname Sage Ssex 学生表
  • OpenCv+VS2017图像处理入门(一)

    安装VS2017 Visual Studio官网下载社区版VS2017 xff0c 安装教程照着网上做即可 安装OpenCv 官网下载opencv3 3 0 vc14 exe 3 配置OpenCv环境 xff08 1 xff09 计算机环境
  • 模板编程:C++11 可变模版参数的使用(逗号表达式、可变长数组、C#的委托实现)

    转载 xff1a 泛化之美 C 43 43 11可变模版参数的妙用 qicosmos 江南 博客园 cnblogs com 重点 xff1a 1 template lt class T gt 和template lt typenameT g
  • Qt------自定义标题栏(最小化、最大化、关闭、边框拉伸、拖动)

    转载 xff1a Qt之界面 xff08 自定义标题栏 无边框 可移动 缩放 xff09 我不是萧海哇 的博客 CSDN博客 参考了两篇文章处理 xff0c 还有半屏没处理 xff0c 就是左右边框的时候半屏 重点 xff1a 1 拖动需要
  • 服务器多解决方案编译

    服务器上的编译方法可以有多种 xff0c 其中一种方案是采用多解决方案 服务器编译项目采用的是多解决方案并行编译 xff0c 而本地编译是根据依赖顺序进行顺序编译 多解决方案的优点 xff1a 一般来说 xff0c 服务器端的软件较为复杂
  • OCC的模型的拓扑结构描述

    如下图所示 一个构件由两个面构成 拓扑结构的描述如下图所示 xff1a Shape gt 面 gt 线框 gt 边 gt 点
  • AutoCad二次开发原理

    重点 xff1a 1 AutoCad主要是三部分组成 xff1a 数据 建模 渲染 2 二次开发AutoCad在建模方面只提供少量的建模接口 xff0c 其中autocad提供ACIS的shell和mesh接口 xff0c 不够使用 需要单
  • Android Studio新建工程默认在build.gradle中加入maven阿里源

    背景 xff1a 在安卓开发时 xff0c 我们时常会因为gradle Sync时间漫长感到烦恼 通常情况下我们会在build gradle Project MyApplication 中的repositories里添加阿里源 xff0c
  • C语言进阶(六):指针数组字符串函数

    目录 一 指针的本质分析 1 号的意义 xff08 1 xff09 指针的声明和使用 xff08 2 xff09 实践 xff1a 指针使用示例 2 传值调用与传址调用 xff08 1 xff09 什么是传值调用 xff0c 传址调用 xf
  • 单片机核心学习:(六)串口UART编程(STM32F103)

    目录 一 硬件知识 UART硬件介绍 1 串口的硬件介绍 2 串口的参数 xff08 1 xff09 怎么发送一字节数据 xff0c 比如 A xff08 2 xff09 逻辑电压 3 串口电平 4 串口内部结构 二 STM32F103 U
  • TortoiseSVN Client使用说明

    1 TortoiseSVN的安装 当前使用的TortoiseSVN为 xff1a TortoiseSVN 1 14 1 29085 x64 svn 1 14 1 msi xff0c 下载安装地址 xff1a Downloads Tortoi
  • 网易云音乐缓存音乐/视频文件的解密操作

    现在播放器的缓存都会进行一定的操作 用户不能简单的通过修改缓存文件名来获取源音乐 视频文件 一般而言 xff0c 即使找到缓存文件并用播放器打开 xff0c 依然不能正常播放 经过笔者小小的观察 xff0c 发现云音乐的缓存文件加密算法十分
  • Linux 网络开发必学课程(一)网络编程的概念与模式、服务端编程初体验

    1 网络编程的概念与模式 网络编程的本质 使用操作系统提供的接口函数 xff0c 使得应用程序具备收发网络数据 的能力 网络接口在代码层面是操作系统提供的函数 网络编程只是一系列系统函数的花式玩法 应用程序通过网络接口使用操作系统的联网能力
  • Linux 网络开发必学课程(六)数据收发的扩展用法

    13 数据收发的扩展用法 xff08 上 xff09 问题 write 和 send 都可以发送数据 xff0c 有什么区别 xff1f read 和 recv 都可以接收数据 xff0c 有什么区别 xff1f 数据收发选项 调用时fla
  • Linux调试私房菜(三)开发中的辅助工具

    六 开发中的辅助工具 1 什么是开发环境 构建环境 代码编写 xff0c 程序编译 xff0c 版本控制 xff08 可选 xff09 调试环境 用于定位问题的辅助工具集测试环境 用于验证目标程序是否满足用户的显性需求和隐性需求 2 嵌入式
  • Linux调试私房菜(九)设计,软件质量之本

    十八 设计 xff0c 软件质量之本 xff08 上 xff09 1 软件设计是什么 xff1f 一种创造性活动 xff0c 力求以简单优雅的方式解决实际问题 软件设计是一门技术 数据结构 xff0c 组成原理 xff0c 操作系统 xff