第8章防范式编程下(代码大全4)

8.4 Exceptions

  异常

  

  用异常通知程序的其他部分,发生了不可忽略的错误

  只在真正例外的情况下才抛出异常

  不能用异常来推卸责任

  避免在构造函数和析构函数中抛出异常,除非你在同一地方把它们捕获

  在恰当的抽象层次抛出异常

  在异常消息中加入关于导致异常发生的全部信息

  避免使用空的catch语句

  了解所用函数库可能抛出的异常

  考虑创建一个集中的异常报告机制

  把项目中对异常的使用标准化

  • 对于像C++这类语言,其中允许抛出多种多样的对象、数据及指针的话,那么就应该为到底可以抛出哪些类的异常建立一个标准。为了和其他语言兼容,可以考虑只抛出Exception基类派生出的对象
  • 考虑创建项目的特定异常类。用作项目中所有可能抛出的异常的基类。这样就能把记录日志、报告错误等操作集中起来并标准化。
  • 规定在何种场合允许代码使用throw - catch语句在局部对错误进行处理。
  • 规定在何种场合允许代码抛出不在局部进行处理的异常。
  • 确定是否要使用集中的异常报告机制。
  • 规定是否允许在构造函数和析构函数中使用异常。

考虑异常的替换方案

  有些程序员用异常来处理错误,只是因为他所用的编程语言提供了这种特殊的错误处理机制。你心里应该自始至终考虑各种各样的错误处理机制:在局部处理错误、使用错误码来传递错误、在日志文件中记录调试错误信息、关闭系统或者其他的一些方式等。

  最后请考虑你的程序是否真的需要处理异常,而不是直接释放所有已获得的资源并终止程序执行,而让用户去重新用正确的输入数据再次运行程序。

8.5 Barricade Your Program to Contain the Damage Caused by Errors

  隔离程序,使之包容由错误造成的损害 

  隔栏(barricade)是一种容损策略(damage-containment strategy)。

  

  在输入数据时将其转换为恰当的类型

Relationship Between Barricade And Assertions

隔栏与断言的关系

  隔栏的使用使断言和错误处理有了清晰的区分。隔栏外部的程序应用错误处理技术,在那里对数据做的任何假定都是不安全的。而隔栏内部的程序里就应该使用断言技术,因为传进来的数据已在通过隔栏时被清理过了。如果隔栏内部的某个子程序检测到了错误的数据,那么这应该是程序里的错误而不是数据里的错误。

  隔栏的使用还展示了“在架构层次上规定应该如何处理错误”的价值。规定隔栏内外的代码是一个架构层次上的决策。

8.6 Debugging Aids

  辅助调试的代码

Don‘t Automatically Apply Production Constraints to the Development Version

不要自动地把产品版的限制强加于开放版之上

  应该在开发期间牺牲一些速度和对资源的使用,来换取一些可以让开发更顺畅的内置工作。

Introduce Debugging Aids Early

尽早引入辅助调试的代码

  

Use Offensive Programming 

采用进攻式编程

  应该以这么一种方式来处理异常情况:在开发阶段让它显现出来,而在产品代码运行时让它能够自我恢复。这就是“进攻式编程”。

  • 确保断言语句使程序终止运行。
  • 完全填充分配到的所有内存,这样可以让你排查出内存分配错误。
  • 确保每一个case语句中的default分支或else分支都能产生严重错误(比如让程序终止运行)。或者至少这些错误不会被忽略。
  • 在删除一个对象之前把它填满垃圾数据。
  • 让程序把它的错误日志文件用电子邮件发送给你,这样你就能了解到在已发布的软件中还发生了哪些错误——如果这对你所开发的软件适用的话。

  有时候,最好的防守正是大胆进攻。在开发时惨痛地失败,能让你发布产品后不会败得太惨。

Plan to Remove Debugging Aids

计划移除调试辅助代码

  使用类似ant和make这样的版本控制工具盒make工具 版本控制工具可以从同一套源代码编译出不同版本的程序。在开发模式下,你可以让make工具把所有的调试代码都包含进来一起编译。而在产品模式下,又可以让make工具把那些你不希望包含在商用版中的调试代码排除在外。

  使用内置的预处理

  编写你自己的预处理器

  使用调试存根

8.7 Determing How Much Defensive Programming to Leave in Production Code

  确定在产品代码中该保留多少防御式代码

  保留哪些检查重要错误的代码

  去掉检查细微错误的代码

  去掉可以导致程序硬性崩溃的代码

  保留可以让程序稳妥地崩溃的代码

  为你的技术支持人员记录错误信息

  确认留在代码中的错误消息是友好的

8.8 Being Defensive About Defensive Programming

  对防御式编程采取防御的姿态

   过度的防御式编程也会引起问题。要考虑好什么地方你需要尽心防御,然后因地制宜地调整你进行防御式编程的优先级。

CHECKLIST:Defensive Programming

核对表:防御式编程

一般事宜

  子程序是否保护自己免遭有害输入数据的破坏?

  你用断言来说明程序假定吗?其中包括了前条件和后条件吗?

  断言是否只是用来说明从不应该发生的情况?

  你是否在架构或高层设计中规定了一组特定的错误处理技术?

  你是否在架构或高层设计中规定了是让错误处理更倾向于健壮性还是正确性?

  你是否建立了隔栏来遏制错误可能造成的破坏?是否减少了其他需要关注错误处理的代码的数量?

  代码中用到辅助调式的代码了吗?

  如果需要启用或禁用添加的辅助助手的话,是否无需大动干戈?

  在防御式编程时引入的代码量是否适宜——既不过多,也不过少?

  你在开发阶段是否采用了进攻式编程来使错误难以被忽略?

异常

  你在项目中定义了一套标准化的异常处理方案吗?

  你是否考虑过异常之外的其他替代方案?

  如果可能的话,是否在局部处理了错误而不是把它当成一个异常抛到外部?

  代码中是否避免了在构造函数和析构函数中抛出异常?

  所有的异常是否都与抛出它们的子程序处于同一抽象层次上?

  每个异常是否都包含了关于异常发生的所有背景信息?

  代码中是否没有使用空的catch语句?(或者如果使用空的catch语句确实很合适,那么明确说明了吗?)

安全事宜

  检查有害输入数据的代码是否也检查了故障的缓冲区溢出、SQL注入、HTML注入、整数溢出以及其他恶意输入数据?

  是否检查了所有的错误返回码?

  是否捕获了所有的异常?

  出错消息中是否避免出现有助于攻击者攻入系统所需的信息。

Key Points

要点

  • 最终产品代码中对错误的处理方式要比“垃圾进、垃圾出”复杂得多。
  • 防御式编程技术可以让错误更容易发现、更容易修改,并减少错误对产品代码的破坏。
  • 断言可以帮助人尽早发现错误,尤其是在大型系统和高可靠性的系统中,以及快速变化的代码中。
  • 关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。
  • 异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,它可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡和比较。
  • 针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快地排查错误的代码。  

第8章防范式编程下(代码大全4)

时间: 2024-10-09 09:59:17

第8章防范式编程下(代码大全4)的相关文章

第8章防范式编程上(代码大全3)

防御式编程并不是说让你在编程时持“防备批评或攻击”的态度——“它就是这么工作!”这一概念来自防御式驾驶.在防御式驾驶中要建立这样一种思维,那就是你永远也不能确定另一位司机将要做什么.这样才能确保其他人在做出危险动作时你也不会受到伤害.你要担负起保护自己的责任,哪怕是其他司机犯的错误.防御式编程的主要思想是:子程序应该不因为传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据.更一般地说,其核心是要承认程序都会有问题,都需要被修改,聪明的程序员应该根据这一点来编程序. 8.1 Protectin

第3章三思而后行:前期准备下(代码大全8)

第3章 Measure Twice, Cut Once:Upstream Prerequisities 三思而后行:前期准备 3.4 需求的先决条件 3.5 架构的先决条件 3.6 花在前期准备上的时间长度 要点 3.4 Requirements Prerequisite 需求的先决条件 软件架构(software architecture)是软件设计的高层部分,是用于支撑更细节的设计的框架. 为什么要把架构作为前期准备工作呢?因为架构的质量决定了系统的“概念完整性”.后者继而决定了系统的最终质

第6章 Android驱动编程

第6章  Android驱动编程 通过介绍本章设备驱动.字符设备驱动编程.GPIO驱动程序实例和4*4扫描键盘驱动等内容,熟练掌握了Android驱动编程.Android内核内核模块编程中包括设备驱动和内核模块.模块相关命令.Android内核内核模块编程和内核模块实例程序.Android内核中采用可加载的模块化设计,一般情况下编译的Android内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中.模块相关命令中lsmod列出了当前系统中加载的模块,rmmood用于当前模块卸载,in

第四章关键的构建决策(代码大全2)

一旦你能确定 “构建”的基础已经打好,那么准备工作就转变为针对特定“构建”的决策了.第3章“三思而后行:前期准备”讨论了设计蓝图和建筑许可证在软件业务里的等价物.你可能对那些准备工作没有多少发言权,所以在第3章关注的焦点是确定“当构建开始后你需要做什么”.本章关注的焦点是程序员和技术带头人个人必须(直接或间接)负责的准备工作.在向工地进发之前,如何选择适用的工作别在你的腰带上,你的手里车里应该装哪些东西?本章讨论的就是这事务在软件中的等价物. 4.1 选择编程语言(Choice of Progr

第七章,shell编程基础

更多笔记点击查看 Linux学习从入门到打死也不放弃,完全笔记整理(持续更新) http://blog.51cto.com/13683480/2095439 笔记整理开始时间:2018年4月12日11:37:35 本章内容: 编程基础 脚本基本格式 变量 运算 条件测试 配置用户环境 编程基础: 程序:指令+数据 程序编程风格: 过程式:以指令为中心,数据服务于指令 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤 一步一步实现,使用的时候一个一个依次调用就可以了 对象式:以数据为中

第十五章、并发编程之线程

目录 第十五章.并发编程之线程 1.什么是线程 2. 进程和线程的区别 3. 开启线程的两种方式 函数开启 类开启 4.子线程与子进程创建速度 5.子线程共享数据的证明 6.线程的join方法 单个子线程 多个子线程 思考 7.了解进程的join 8. 线程的其他相关用法 第十五章.并发编程之线程 1.什么是线程 纠正概念:进程其实不是个执行单位,进程是一个资源单位,每个进程内自带一个线程,线程才是cpu上的执行单位 抽象理解: 进程是指在系统中正在运行的一个应用程序:线程是系统分配处理器时间资

Android 编程下 App Install Location

从 API 8 开始(参考官方文档:App Install Location | Android Developers),你可以将你的应用安装在外部储存中(例如,安装到设备的 SD 卡上).这是一个可选的特征,你可以在你的应用的 AndroidManifest.xml 中声明 android:installLocation 属性.如果你没有声明这个属性,你的应用程序将会被安装在内部储存,并且不能被移到外置储存中. 修改 AndroidManifest.xml 文件中 <manifest> 元素

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

Android 编程下如何调整 SwipeRefreshLayout 的下拉刷新距离

SwipeRefreshLayout 的下拉刷新距离比较短,并且也没有提供设置下拉距离的 API,但是看 SwipeRefreshLayout 的源码,会发现有一个内部变量 mDistanceToTriggerSync,这个变量决定了触发刷新的下拉距离.下面的代码展示了源码中是如何给这个变量赋值的: final DisplayMetrics metrics = getResources().getDisplayMetrics(); mDistanceToTriggerSync = (int) M