防御型编程

本文探讨防御性编程策略,包括使用断言进行错误排查,以及单元测试的设计与实施,确保代码健壮性和功能正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整理自《C++编程思想》:防御性编程一章

简介

防御性编程:寓意为编写具有健壮性的程序,本节介绍常用的方法

1. 断言

作用:排查项目程序设计错误(发布的程序最好没有断言)
举例:
测试人员使用的测试版本,并不包含断言功能。当测试发现问题后,研发人员可以提供断言版本的程序,复现问题并定位原因

1.1 断言定义

断言格式:
assert(expression);
当表达式expression为true,代码继续运行
当表达式expression为false,打印出调试信息,然后程序退出
调试信息格式为:
sourcefilename:line: function: Assertion `[#cond…str]’ failed.

1.2 设计断言

#include <cstdio>
#include <cstdlib>
#include <iostream>
void __my_assert(char const *srcfile,unsigned int line, char const *func,
char const *assert)
{
	using namespace std;
	cout << srcfile << ": in line "<<  line << " function: "<< func <<" Assertion [ "
	<<  assert << " ] failed" << endl; 

	abort();
	
}

#ifdef NDEBUG
#define assert(cond) ((void)0)
#else
#define assert(cond) ((cond)? (void)0 : __my_assert(__FILE__,__LINE__,__FUNCTION__,#cond))
#endif

int main(void)
{
	bool cond;
	/*OK*/
	assert(cond = true);
	/*Nok*/
	assert(cond = false);
	return 0;
}

输出:
assert.cpp: in line 28 function: main Assertion [ cond = false ] failed
Aborted (core dumped)

1.3 系统工具

标准C库支持一个断言函数

NAME
      assert - abort the program if assertion is false

SYNOPSIS
      #include <assert.h>

      void assert(scalar expression);

DESCRIPTION
      If  the  macro  NDEBUG  was  defined  at the moment <assert.h> was last
      included, the macro assert() generates no code, and hence does  nothing
      at all.  Otherwise, the macro assert() prints an error message to stan‐
      dard error and terminates the program by calling abort(3) if expression
      is false (i.e., compares equal to zero).

      The  purpose  of  this  macro is to help programmers find bugs in their
      programs.  The  message  "assertion  failed  in  file  foo.c,  function
      do_bar(), line 1287" is of no help at all to a user.

RETURN VALUE
      No value is returned.

ATTRIBUTES
      For   an   explanation   of   the  terms  used  in  this  section,  see
      attributes(7).

      ┌──────────┬───────────────┬─────────┐
      │Interface │ Attribute     │ Value   │
      ├──────────┼───────────────┼─────────┤
      │assert()  │ Thread safety │ MT-Safe │
      └──────────┴───────────────┴─────────┘

CONFORMING TO
      POSIX.1-2001, POSIX.1-2008, C89, C99.  In C89, expression  is  required
      to  be  of type int and undefined behavior results if it is not, but in
      C99 it may have any scalar type.

2. 单元测试

作用:测试代码片段的功能实现,边界测试(发布的程序不包含单元测试代码)
举例:
需求变动添加或者改动函数接口。为了针对性测试特定接口功能,单独编写的测试代码

2.1 设计单元测试

源码目录如下:
在这里插入图片描述
编译方法:g++ Date.cpp DateTest.cpp TestSuite/test.cpp
输出结果:
Report for Class: 8DateTest
3[Passed tests] 0[Failed tests]

  1. 首先设计一个类 Date
    写一个与日期有关得类,它将包含以下接口
/*Date.h*/
#include <string>

class Date
{
public:
   struct Duration
   {
   public:
   	int years;
   	int months;
   	int days;
   	Duration(int ys,int ms,int ds);
   };

   int m_year;
   int m_month;
   int m_day;
   Date();
   Date(int year,int month,int day);
   Date(std::string const& date);
   
   virtual ~Date();

   int getYear() const;

   int getMonth() const;
   int getDay() const;

   std::string toString() const;

   friend bool operator > (Date const& lfs,Date const &rhs);
   friend bool operator < (Date const& lfs,Date const &rhs);
   friend bool operator >= (Date const& lfs,Date const &rhs);
   friend bool operator <= (Date const& lfs,Date const &rhs);
   friend bool operator == (Date const& lfs,Date const &rhs);
   friend bool operator != (Date const& lfs,Date const &rhs);

   friend Duration duration(Date const& lfs, Date const& rhs);

};
/*Date.cpp*/
#include <string>
#include "Date.h"
/*
	struct Duration
	{
	public:
		int years;
		int months;
		int days;
		Duration(int ys,int ms,int ds);
	};

	int m_year;
	int m_month;
	int m_day;
*/
	Date::Date(){}
	Date::Date(int year,int month,int day):m_year(year),m_month(month),m_day(day){}
	Date::Date(std::string const& date){}
	Date::~Date(){}

	int Date::getYear() const
	{
		return m_year;
	}
	int Date::getMonth() const
	{
		return m_month;
	}
	int Date::getDay() const
	{
		return m_day;
	}

	std::string Date::toString() const
	{
	}

	bool operator > (Date const& lfs,Date const &rhs);
	bool operator < (Date const& lfs,Date const &rhs);
	bool operator >= (Date const& lfs,Date const &rhs);
	bool operator <= (Date const& lfs,Date const &rhs);
	bool operator == (Date const& lfs,Date const &rhs);
	bool operator != (Date const& lfs,Date const &rhs);

	Date::Duration duration(Date const& lfs, Date const& rhs);

  1. 先写测试
    第一步:写通用测试接口(可以从互联网上下载开源框架)
/*TestSuite/test.h*/

#include <iostream>
#include <typeinfo>

using std::string;
using std::ostream;
using std::cout;
namespace TestSuite
{
	#define test_(cond) do_test((cond),#cond,__FILE__,__LINE__)
	#define fail_(str) do_fail(#str,__FILE__,__LINE)

	class test	
	{
	public:
		test(void);
		virtual ~test (void);
		virtual void run(void) = 0;

		void do_test(bool cond, char const *cond_str,char const *file,int line);

		void do_fail(char const *info,char const *file, int line);
		void succeed_(void);
		int getnum_Passed(void);
		int getnum_Failed(void);
		void report(void);

		void reset(void);

		ostream *set_Ostream(ostream* new_os);
		ostream* get_Ostream(void);
	private:
		int m_nPassed;
		int m_nFailed;
		ostream * ostr;
		test(test const& that);
		test & operator= (test const& rhs);
	};
};
/*TestSuite/test.cpp*/
#include <iostream>
#include <typeinfo>
#include "test.h"

namespace TestSuite
{

		test::test(void) : m_nPassed(0),m_nFailed(0),ostr(&cout){}
		test::~test (void){}

		void test::do_test(bool cond, char const *cond_str,char const *file,
		int line)
		{
			
			cond? succeed_() : do_fail(cond_str,file,line);
		}

		void test::do_fail(char const *info,char const *file, int line)
		{
			m_nFailed++;
			*ostr << typeid(*this).name() << ":" << std::endl;
			*ostr << "Line : " << line << "Under File " 
			<< file << " Failed [" << info << "]" << std::endl; 
		}

		void test::succeed_(void)
		{
			m_nPassed++;
		}
		int test::getnum_Passed(void)
		{
			return m_nFailed;
		}
		int test::getnum_Failed(void)
		{
			return m_nPassed;
		}
		void test::report(void)
		{
			*ostr << "Report for Class: " << typeid(*this).name() << std::endl;
			*ostr << m_nPassed << "[Passed tests] "  << m_nFailed << "[Failed tests]"
			<< std::endl;
		}

		void test::reset(void)
		{
			m_nPassed=m_nFailed=0;
		}

		ostream *test::set_Ostream(ostream* new_os)
		{
			ostream *old = ostr;
			ostr=new_os;
			return old;
		}
		ostream* test::get_Ostream(void)
		{
			return ostr;
		}
}
  1. 写Date测试接口
/*DateTest.cpp*/
#include "Date.h"
#include "TestSuite/test.h"

class DateTest : public TestSuite::test
{
public:
	DateTest(void):m_iday(2020,1,6)
	{
	}
	~ DateTest(void){}
	void run(void)
	{
		test_(m_iday.getYear() == 2020);
		test_(m_iday.getMonth() == 1);
		test_(m_iday.getDay() == 6);
	}
	
private:
Date m_today;
Date m_strday;
Date m_iday;
};

int main()
{
	DateTest test;
	test.run();
	test.report();

	return 0;
}

2.2 设计单元测试套件

2.1 一节利用class test设计了class DateTest并进行了测试
通常一个程序设计中应用到多个类,就应当设计多个测试类
在这里插入图片描述
这样可以再设计一个类把多个测试类封装在一起集中测试叫做Suite
代码设计略…
在这里插入图片描述
在这里插入图片描述

2.3 第三方UT工具

参考:
https://blog.youkuaiyun.com/u013176681/article/details/49661667

### React中实现防御型编程的最佳实践 #### 什么是防御型编程防御型编程是一种软件开发技术,旨在通过编写健壮的代码来防止程序因意外输入或状态而崩溃。其核心理念是在代码中加入额外的安全措施,以验证数据的有效性和程序的状态。 在React应用中实施防御型编程可以通过多种方式完成,包括但不限于: --- #### 1. 输入验证 确保组件接收到的数据始终处于预期范围内。这可以通过`PropTypes`或TypeScript静态类型检查工具实现。 ```javascript import PropTypes from 'prop-types'; function Greeting({ name }) { return <h1>Hello, {name}</h1>; } Greeting.propTypes = { name: PropTypes.string.isRequired, }; ``` 如果未提供必需属性,则会在控制台发出警告[^1]。 对于更复杂的场景,可以使用TypeScript定义严格的类型约束: ```typescript interface Props { name?: string; } const Greeting: React.FC<Props> = ({ name }) => { if (typeof name !== 'string') { throw new Error('Invalid prop `name`: Expected a string.'); } return <h1>Hello, {name || 'Guest'}</h1>; }; ``` --- #### 2. 默认值设定 当某些参数可能为空时,应为其指定默认值以避免潜在错误。 ```javascript function UserCard({ username = 'Unknown', age = 0 }) { return ( <div> <p>Name: {username}</p> <p>Age: {age >= 0 ? age : 'Not provided'}</p> </div> ); } ``` 此方法有助于处理不确定性的输入,并增强用户体验[^3]。 --- #### 3. 错误边界(Error Boundaries) React提供了专门用于捕获子树渲染期间抛出错误的功能——Error Boundaries。它们允许开发者优雅地显示备用UI而不是使整个应用程序崩溃。 ```javascript class ErrorHandler extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { console.error("Caught an error:", error, info.componentStack); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } // 使用示例 <ErrorHandler> <ComponentThatMayFail /> </ErrorHandler> ``` 这种机制非常适合大型复杂的应用程序,在其中局部异常不应影响全局稳定性[^4]。 --- #### 4. 条件渲染与短路运算符 利用条件语句和逻辑操作符简化视图层逻辑的同时也增加了安全性。 ```javascript function DisplayData({ data }) { return ( <> {(data && typeof data === 'object') ? ( Object.keys(data).map(key => <p key={key}>{`${key}: ${data[key]}`}</p>) ) : ( <p>No Data Available</p> )} </> ); } ``` 这里我们先确认`data`存在且为对象形式后再尝试访问其键值对,从而规避非法操作引发的问题。 --- #### 5. 不信任外部API返回的结果 任何时候调用第三方服务都需谨慎对待结果集,因为这些源往往不可控。 ```javascript async function fetchUserData(userId) { try { const response = await axios.get(`/api/users/${userId}`); // 数据校验 if (!response.data || !Array.isArray(response.data)) { throw new Error('Unexpected API Response'); } return response.data.map(user => ({ id: user.id ?? null, email: user.email?.toLowerCase().trim(), })); } catch (error) { console.warn(`Failed to load users with ID=${userId}`, error.message); return []; } } ``` 上述例子展示了如何安全解析来自网络请求的信息以及妥善管理失败情形下的回退方案[^2]。 --- #### 6. 单元测试覆盖边缘情况 最后但同样重要的是要建立全面的单元测试体系去发现隐藏缺陷。 ```javascript test('renders fallback content when no props are passed', () => { const wrapper = shallow(<DisplayData />); expect(wrapper.text()).toContain('No Data Available'); }); test('handles malformed input gracefully', () => { const badInput = {}; const wrapper = mount(<DisplayData data={badInput} />); expect(() => wrapper.update()).not.toThrow(); }); ``` 良好的测试习惯能有效降低生产环境中的风险概率[^3]。 --- ### 总结 以上列举了几种适用于React框架内的防御性编码技巧,每一种都有助于构建更加稳定可靠的前端解决方案。当然实际项目可能会遇到更多特殊需求,因此灵活运用才是关键所在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值