0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

防御性编程:让系统坚不可摧

京东云 来源:jf_75140285 作者:jf_75140285 2024-07-25 14:04 次阅读

1. 引言

面对复杂多变的运行环境、不可预测的用户输入以及潜在的编程错误,如何确保软件在遭遇异常情况时依然能够稳定运行,是每位开发者必须面对的挑战。防御性编程(Defensive Programming)正是为解决这一问题而生的一种编程范式,它强调在编程过程中预见并防范潜在的错误和异常情况,从而增强软件的健壮性和稳定性。作为一种细致、谨慎的编程方法,通过提前考虑并防范可能出现的错误,从而有效减少软件漏洞和故障。本文将详细介绍防御性编程的基本概念、关键策略,并通过实际案例展示其在实际项目中的应用。

2. 防御性编程的基本概念

防御性编程的核心思想在于承认程序总会存在问题和需要修改,因此聪明的程序员会提前考虑并防范可能的错误。它强调在编程过程中不仅要实现功能,还要确保程序在面对错误输入、异常情况和并发操作时能够稳定运行

3. 防御性编程的核心原则

3.1 风险识别

非系统性风险:只影特定场景下的响单次调用,不对系统整体稳定性产生影响。比如空指针异常、数据越界等。

系统性风险:导致整个服务不可用的风险。比如 死循环,分页查询pageSize过大等。

3.2 防御原则

1.假设输入总是错误的:不依赖外部输入的绝对正确性,对所有输入进行验证和清理。

2.最小化错误的影响范围:通过异常处理、错误隔离等措施,限制错误对系统整体的影响。

3.使用断言进行内部检查:在代码的关键位置加入断言,确保程序状态符合预期。

4.代码清晰易懂:编写易于理解和维护的代码,便于团队成员发现潜在问题。

5.持续测试:通过单元测试、集成测试等手段,不断验证软件的正确性和稳定性。

4. 防御性编程案例

4.1 输入验证与清理

场景

用户输入数据到Web表单中,系统需要处理这些数据以进行后续操作。

防御性编程实践

风险识别:系统性风险,可能导致系统整体不可用。

防御策略

验证数据类型:确保用户输入的数据类型符合预期(如数字、字符串、日期等)。如果类型不匹配,应给出错误提示并要求用户重新输入。

长度和范围检查:对于字符串、数字等类型的数据,进行长度和范围检查,确保它们不超过系统处理能力的限制。

清理输入数据:去除输入数据中的非法字符或格式,如去除字符串两端的空格、将特殊字符转换为普通字符等。

分页参数防御式编程案例

下面以分页参数防御式编程为案例进行举例说明:

场景描述: 假设开发一个Web API,该API需要根据用户请求返回特定数据的分页结果。分页请求包含以下参数:

•pageSize:每页应显示的记录数。

•pageNumber:用户请求的当前页码。

防御性编程措施

1.验证pageSize:确保pageSize是一个正整数,并且不超过一个合理的最大值(例如100),以防止资源过度消耗。

2.验证pageNumber:确保pageNumber是一个正整数,并且不会请求到不存在的页码(即基于总记录数和pageSize计算出的最大页码之后)。

3.处理无效参数:如果参数无效,则返回清晰的错误消息,并可能设置一个默认的页码或每页记录数。

4.计算总页数:基于总记录数和pageSize计算总页数,以便在返回分页信息时包含给用户。

示例代码(伪代码):

public class PaginationService {        
    private static final int MAX_PAGE_SIZE = 100;        
        /**       
         * 获取分页信息并进行参数校验       
         *        
         * @param totalRecords 总记录数       
         * @param pageSize 每页记录数       
         * @param pageNumber 当前页码       
         * @return 分页信息,包括总页数、当前页码等       
         */      
         public PaginationInfo getPaginationInfo(int totalRecords, int pageSize, int pageNumber) {          
             // 校验pageSize          
             if (pageSize <= 0 || pageSize > MAX_PAGE_SIZE) {              
                 throw new IllegalArgumentException("pageSize必须为正整数且不超过" + MAX_PAGE_SIZE);          
             }            
             // 校验pageNumber          
             if (pageNumber <= 0) {              
                 pageNumber = 1; // 默认为第一页          
             }            
             // 计算总页数          
             int totalPages = (totalRecords + pageSize - 1) / pageSize;            
             // 确保pageNumber不超过总页数          
             if (pageNumber > totalPages) {              
                 pageNumber = totalPages;          
             }            
             // 计算当前页的数据起始索引(可选,根据具体需求)          
             int startIndex = (pageNumber - 1) * pageSize;            
             // 返回分页信息          
             return new PaginationInfo(totalPages, pageNumber, startIndex);      
         }        
         // PaginationInfo 是一个简单的类,用于封装分页信息 
         ...

在这个例子中,getPaginationInfo 方法首先验证了 pageSize 和 pageNumber 参数的有效性,确保了它们符合预期的约束条件。如果参数无效,方法会抛出一个 IllegalArgumentException 异常,这有助于调用者识别并处理错误情况。然后,方法计算了总页数,并根据需要调整了 pageNumber 以确保它不会超出范围。最后,方法返回了一个包含分页信息的 PaginationInfo 对象。

这种防御性编程策略有助于防止因无效的分页参数而导致的程序错误,提高了API的健壮性和用户体验。

4.2 预防死循环

场景

在循环或者遍历场景中,没有明确的退出机制。

防御性编程实践

风险识别:系统性风险,可能导致系统整体不可用。

防御策略

参数验证:检查涉及循环步长的入参是否有效。

循环终止条件必达性确认:在涉及条件校验的场景中,避免等值条件判断,防止跳过循环终止点。

日志记录:在关键位置添加日志记录,帮助调试和追踪问题。

示例代码Java):

/**  
 * 生成时间段。  
 *  
 * @param startMinutes 开始时间
 * @param endMinutes 结束时间
 * @param interval 时间段间隔
 * @param duration 时间的时长
 * @return 时间段列表 
 */  
public List< String > generateList(int startMinutes, int endMinutes, int interval, int duration) {

    List< String > result = new ArrayList<  >();
    int nextStartTime = startMinutes;

    while (nextStartTime == endMinutes) {
        int currentStartMinutes = nextStartTime;
  
        int currentEndMinutes = currentStartMinutes + duration;

        result.add(currentStartMinutes + "-" + currentEndMinutes);
  
        nextBatchStartTime += interval;
    }
    return result;
}

针对以上代码,我们可以添加一些防御式编程的元素来确保代码的健壮性和可靠性。防御式编程侧重于预防错误的发生,包括输入验证、错误处理和边界条件检查。以下是修改后的代码,包含了防御式编程的改进:

/** 
 * 生成时间段。 
 * 
 * @param startMinutes 开始时间
 * @param endMinutes 结束时间
 * @param interval 时间段间隔
 * @param duration 时间的时长
 * @return 时间段列表 
 */ 
public List< String > generateList(int startMinutes, int endMinutes, int interval, int duration) {
    // 改进点1:校验 interval,以保证循环中的步长能够正向增长
    // 一般情况下,还需要对步长,和 endMinutes与startMinutes的区间大小做限制,避免生成“巨大”的列表。
    if (interval <= 0) {
        throw new IllegalArgumentException("Invalid parameters: interval must be positive integers.");
    }
    List< String > result = new ArrayList<  >();
    int nextStartTime = startMinutes;

    //改进点2:避免使用等号做循环终止条件,以防跳过循环终止点。
    while (nextStartTime <= endMinutes) {
        int currentStartMinutes = nextStartTime;
  
        int currentEndMinutes = currentStartMinutes + duration;

        result.add(currentStartMinutes + "-" + currentEndMinutes);
  
        nextBatchStartTime += interval;
    }
    return result;
}

4.3 异常处理

场景

程序在读取文件、进行网络请求或执行其他可能失败的操作时,需要处理潜在的异常。

防御性编程实践

风险识别:非系统性风险,影响单次请求。

防御策略

使用try-except语句:将可能抛出异常的代码块放在try语句中,并在except语句中捕获并处理这些异常。

区分异常类型:根据实际需要捕获特定的异常类型,或捕获所有异常(使用Exception作为异常类型)。

记录错误信息:在捕获异常后,记录详细的错误信息(如异常类型、错误消息、堆栈跟踪等),以便后续分析和调试。

示例代码(Java):

/**  
 * 读取文件内容。  
 *  
 * @param filePath 文件路径  
 * @return 文件内容,如果文件不存在或读取失败则返回null  
 */  
public static String readFile(String filePath) {  
    try {  
        byte[] encoded = Files.readAllBytes(Paths.get(filePath));  
        return new String(encoded);  
    } catch (FileNotFoundException e) {  
        log.info("文件未找到:" + filePath);  
        return null;  
    } catch (Exception e) {  
        log.info("读取文件时发生错误:" + e.getMessage());  
        return null;  
    }  
}  

4.4 边界条件检查

场景

在循环、条件判断或数组访问等操作中,需要确保不会超出预期的范围或边界。

防御性编程实践

风险识别:非系统性风险,影响单次请求。

防御策略

检查循环条件:确保循环条件在每次迭代后都能正确更新,以避免无限循环。

数组和集合访问:在访问数组、列表、字典等集合的元素之前,检查索引或键是否有效。

边界值测试:对函数或方法的输入进行边界值测试,以确保它们在边界条件下也能正常工作。

示例代码(Java):

public class ArrayAccess {      
    public static void main(String[] args) {          
        int[] numbers = {1, 2, 3, 4, 5};          
        int index = getIndexFromUser(); // 假设这是从用户那里获取的索引                    
        if (index >= 0 && index < numbers.length) {              
            log.info(numbers[index]);          
        } else {              
            log.info("索引超出数组范围");          
        }      
    }                 
    
    // 假设这个方法从用户那里获取索引值,并进行基本的验证 
    private static int getIndexFromUser() {       
        // 为了示例,我们直接返回一个示例值          
        return 2; // 假设用户输入了有效的索引值2      
    }  
}

4.5 使用断言进行内部检查

场景

在代码的关键路径上,需要确保某些条件始终为真,否则程序将无法正确执行。

防御性编程实践

使用断言:在代码的关键位置添加断言(如Python的assert语句),以验证程序状态是否符合预期。如果断言失败,则抛出AssertionError异常。

注意断言的使用场景:断言主要用于开发和测试阶段,用于捕获那些理论上不应该发生的错误。在生产环境中,应该依赖更健壮的错误处理机制。

示例代码(Java):

/**  
 * 计算年龄。  
 *  
 * @param birthYear 出生年份  
 * @return 年龄,如果输入无效则返回-1。  
 */  
public static int calculateAge(int birthYear) {  
    // 输入验证:确保出生年份是一个合理的值  
    if (birthYear <= 0 || birthYear > java.time.Year.now().getValue()) {  
        // 抛出IllegalArgumentException来指示方法接收到了非法参数  
        throw new IllegalArgumentException("出生年份必须是一个大于0且小于当前年份的整数");  
    }  
  
    // 计算年龄  
    int currentYear = java.time.Year.now().getValue();  
    return currentYear - birthYear;  
}  
  
public static void main(String[] args) {  
    try {  
        // 假设我们从某个地方(如用户输入)获取了出生年份  
        int birthYear = 1990; // 这里直接赋值作为示例  
  
        int age = calculateAge(birthYear);  
            if (age != -1) { // 注意:这个例子中calculateAge实际上不会返回-1,但为了展示如何处理可能的异常情况,我们可以这样设计  
                log.info("年龄是:" + age);  
            }  
  
        } catch (IllegalArgumentException e) {  
            // 捕获并处理IllegalArgumentException  
            log.info("错误:" + e.getMessage());  
        }  
  
        // 如果需要从用户输入中获取出生年份,你可以添加相应的逻辑来处理字符串到整数的转换和验证  
    }  
  
    // 注意:在这个例子中,我们没有直接使用assert,因为Java的assert主要用于调试,且默认是禁用的。  
    // 而是通过显式的条件检查和异常抛出来实现防御性编程。  

5. 防御式编程的挑战

5.1 是不是防御式代码越多越好呢?

No,过度的防御式编程会使程序会变得臃肿而缓慢,增加软件的复杂度。

要考虑好什么地方需要进行防御,然后因地制宜地调整进行防御式编程的优先级。

一般在入口处或者接入层做通用性防御性编程,比如数据准入校验;但对于循环类逻辑,应始终在使用处做细节性防御

5.2 通用性防御措施 优于 细节性的防御

例如对于网络请求,一般是统一处理超时、鉴权、各种错误code,而不是在业务层个别处理

5.3 根据使用场景,调整防御力度

如项目内部使用的utils函数和公开发布的package,后者防御要求更高

6. 结论

防御性编程是一种积极主动的编程策略,它要求开发者在编写代码时,不仅要关注功能的实现,更要关注代码的健壮性和稳定性。通过预见并防范潜在的错误和异常情况,防御性编程能够显著提升软件的质量,减少因外部因素导致的程序崩溃,提升系统稳定性。

文章中难免会有不足之处,希望读者能给予宝贵的意见和建议。谢谢!

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3614

    浏览量

    93686
收藏 人收藏

    评论

    相关推荐

    ‌水库大坝安全监测系统的功能有什么

    在浩瀚的自然与人工交织的水利网络中,水库大坝作为关键节点,其安全稳定运行关乎国计民生。为了保障这一水利巨擘的稳固,水库大坝安全监测系统以科技之力筑起一道坚不可摧的水利防线。
    的头像 发表于 12-07 15:32 129次阅读

    探索分布式IO模块的介质冗余:赋能工业自动化的稳健之心

    凸显。明达技术自主研发的带有介质冗余功能的MR30分布式IO模块,正以其独特的优势,为工业自动化系统构建起一道坚不可摧的防护网。
    的头像 发表于 09-25 10:45 245次阅读
    探索分布式IO模块的介质冗余:赋能工业自动化的稳健之心

    揭秘!霍尼韦尔MIP系列—无坚不摧的介质隔离压力传感器冷却液、腐蚀介质环境

    揭秘霍尼韦尔MIP系列——无坚不摧的介质隔离压力传感器,引领工业监测新纪元在极端工业环境的洪流中,有这样一款传感器,它如同城堡般坚不可摧,名为霍尼韦尔MIP系列介质隔离压力传感器。这款传感器不仅是
    的头像 发表于 08-31 08:06 600次阅读
    揭秘!霍尼韦尔MIP系列—无坚不摧的介质隔离压力传感器冷却液、腐蚀<b class='flag-5'>性</b>介质环境

    微软推出企业版付费人脸识别技术Face Check

    微软近日宣布,其先进的人脸识别技术Face Check已全面向全球企业客户开放,标志着微软在身份认证安全领域迈出了重要一步。Face Check技术通过融合用户手机自拍与已验证的身份照片,为企业构建了一道坚不可摧的身份验证防线。
    的头像 发表于 08-14 17:38 1046次阅读

    特信反制无人机干扰设备防御策略

    无人机干扰设备的防御策略是一个综合的过程,涉及多个方面的技术和措施。以下是一些主要的防御方法:
    的头像 发表于 08-01 09:20 395次阅读

    深圳滤波器厂家:电子设备的纯净守护者

    在电子设备日益普及的今天,电源滤波器作为保障电力纯净与稳定的关键元件,其重要愈发凸显。电源滤波器,顾名思义,是一种专门用于过滤电源中杂波和干扰信号的电子装置。它如同一道坚不可摧的防线,为电力系统提供了纯净的能源环境,确保各类电
    的头像 发表于 07-10 09:28 207次阅读
    深圳滤波器厂家:电子设备的纯净守护者

    深圳特信无人机反制与无人机干扰系统如何保护一线城市安全

    上空的安全与秩序。无论是保障重要活动的顺利进行,还是维护市民的日常安宁,深圳特信都以其卓越的技术实力,为一线城市的安宁筑起坚不可摧的防线。
    的头像 发表于 07-09 09:10 322次阅读

    无人机主动防御系统不起作用吗

    起作用。无人机主动防御系统是一种用于保护无人机免受攻击的系统。这种系统可以有效地防止无人机被敌方攻击,提高无人机的生存能力。然而,无人机主动防御
    的头像 发表于 07-08 09:57 511次阅读

    无人机主动防御系统有什么作用

    无人机主动防御系统是一种用于保护无人机免受攻击或干扰的系统。这种系统可以提高无人机的安全和可靠
    的头像 发表于 07-08 09:54 598次阅读

    无人机主动防御系统有哪些

    无人机主动防御系统是一种用于保护无人机免受攻击的系统。随着无人机在军事、民用和商业领域的广泛应用,无人机的安全问题也日益凸显。本文将介绍无人机主动防御
    的头像 发表于 07-08 09:50 1060次阅读

    无人机主动防御系统安装需要备案吗

    无人机主动防御系统是一种用于保护无人机免受攻击的系统,它可以有效地防止无人机被黑客攻击、干扰、劫持等。在安装无人机主动防御系统时,需要考虑以
    的头像 发表于 07-08 09:46 380次阅读

    知语云智能科技无人机防御系统:应对新兴威胁的先锋力量

    随着科技的飞速发展,无人机技术在各个领域的应用日益广泛,但随之而来的是无人机威胁的不断升级。为了有效应对这些新兴威胁,知语云智能科技推出了先进的无人机防御系统,为空中安全保驾护航。 无人机防御
    发表于 02-26 16:35

    虚拟化软件栈有哪些防御措施

    软件栈的防御措施。 基础防御措施 强化操作系统安全性:虚拟化软件栈的基础是操作系统,因此首先需要确保操作系统的安全
    的头像 发表于 01-25 11:27 756次阅读

    【虹科分享】一种动态防御策略——移动目标防御(MTD)

    文章主要探讨了网络安全领域中的动态防御策略,特别是针对规避威胁的“移动目标防御”(MTD)技术。本文分析了攻击者常用的规避技术,并探讨了如何利用移动目标防御技术来对抗这些威胁。传统的
    的头像 发表于 01-04 14:07 559次阅读
    【虹科分享】一种动态<b class='flag-5'>防御</b>策略——移动目标<b class='flag-5'>防御</b>(MTD)

    一种动态防御策略——移动目标防御(MTD)

    网络攻击的技术变得愈发难测,网络攻击者用多态、混淆、加密和自我修改乔装他们的恶意软件,以此逃避防御性的检测,于是移动目标防御(MTD)技术出现了,通过动态地改变攻击面,有效地对抗日益复杂和隐蔽
    的头像 发表于 01-04 08:04 1474次阅读
    一种动态<b class='flag-5'>防御</b>策略——移动目标<b class='flag-5'>防御</b>(MTD)