如何写出好代码 — 防御式编程指南
引言
墨菲定律如果有两种或两种以上的方式去做某件事情,而其中一种选择方式将导致灾难,则必定有人会做出这种选择。这是一种偏悲观的思想,认为所有可能出问题的坏情况都会发生。那么在抱有该想法去做设计时,就需要对最坏情况做出预测并采取相对应的措施。同时让使用人员不需要进行复杂的思考,通过直觉即可使用某个系统,即所谓的防呆设计。如3.5寸的软盘设计,就设计成只有一种情况可以插入进去。
什么是防御式编程?
防御式编程和防御式驾驶防御式编程,这一概念来自防御式驾驶。在防御式驾驶中要建立这样一种思维,那就是你永远也不能确定另一位司机将要做什么。这样才能确保在其他人做出危险动作时你也不会受到伤害。你要承担起保护自己的责任,哪怕是其他司机犯的错误。
Class Main {
private Connection con = = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
public List<Student> doQuery(String name) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, grade, name, gender FROM students WHERE name=" + name);
List<Student> studentList = new ArrayList<>();
while(rs.next()) {
long id = rs.getLong(1);
long grade = rs.getLong(2);
String name = rs.getString(3);
String gender = convertGender(rs.getInt(4));
Student student = new Student(id, grade, name, gender);
studentList.add(student);
}
return studentList;
}
private String convertGender(int gender) {
switch(gender) {
case 0 : return "male";
case 1 : return "female";
}
return null;
}
}
边界防御:检查所有的外部输入
异常处理:在正确性和健壮性之间做好取舍
- 正确性是指:程序永不返回不准确的结果,即使这样做会不返回结果或是直接退出程序。
- 健壮性是指:系统在不正常的输入或不正常的外部环境下仍能正常运行,哪怕输出结果是错误的或者不完整的。
应检尽检:没有完全可靠的外部环境
显示约束:简单直接的代码风格
减少依赖:write once, run anywhere
傻瓜式注释
契约式编程
- 前置条件:期望所有调用它的客户模块都保证一定的进入条件,比如非NULL、非0等要求;
- 后置条件:保证退出时给出特定的属性,比如程序退出时会释放数据库连接;
- 不变式:在进入时假定,并在退出时保持一些特定的属性。
契约式编程是比防御式编程更加乐观的一种编程思路,强调约定和断言,具体想了解契约式编程的同学,可以移步:https://www.eiffel.com/values/design-by-contract/introduction/
避免过度设计
总结
-
提高工程质量——减少bug和问题;
-
提高源码可读性—— 源码应该变得可读且可理解,并且能经受code review;
-
让软件能通过预期的行为来处理不可预期的用户操作。
附录:防御式编程checkList
-
子程序是否保护自己免遭有害输入数据的破坏?
-
你用断言来说明编程假定吗?其中包括了前条件和后条件吗?
-
断言是否只是用来说明从不应该发生的情况?
-
你是否在架构或高层设计中规定了一组特定的错误处理技术?
-
你是否在架构或高层设计中规定了是让错误处理更倾向于健壮性还是正确性?
-
你是否建立了隔栏来遏制错误可能造成的破坏?是否减少了其他需要关注错误处理的代码的数量?
-
代码中用到辅助调试的代码了吗?
-
如果需要启用或禁用添加的辅助助手的话,是否无需大动干戈?
-
在防御式编程时映入的代码量是否适宜–既不过多,也不过少?
-
在开发阶段是否采用了进攻式编程来使错误难以被忽视?
-
你在项目中定义了一套标准化的异常处理方案吗?
-
是否考虑过异常之外的其他替代方案?
-
如果可能的话,是否在局部处理了错误而不是把它当成一个异常抛到外部?
-
代码中是否避免了在构造函数和析构函数中抛出异常?
-
所有的异常是否都与抛出它们的子程序处于同一抽象层次上?6).每个异常是否都包含了关于异常发生的所有背景信息?
-
代码中是否没有使用空的catch语句?(或者如果使用空的catch语句确实很合适,那么明确说明了吗?)
-
检查有害输入数据的代码是否也检查了故意的缓冲区溢出、SQL注入、HTML注入、证书溢出一级其他恶意输入数据?
-
是否检查了所有的错误返回码
-
是否捕获了所有的异常?
-
出错消息中是否避免出现有助于攻击者攻入系统所需的信息?
-
最终产品代码中对错误的处理方式要比“垃圾进,垃圾出”复杂的多。
-
防御式编程技术可以让错误更容易发现、更容易修改,并减少错误对产品代码的破坏。
-
断言可以帮助人尽早发现错误,尤其是在大型系统和高可靠性的系统中,以及快速变化的代码中。
-
关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。
-
异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,它可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。
-
针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快地排查错误的代码。
作者简介
云智慧架构部,长期致力于智能运维领域工程架构的建设和发展,打造高性能、高可用、高易用的运维工程框架,提升公司技术水位线。
开源福利
云智慧已开源数据可视化编排平台 FlyFish 。通过配置数据模型为用户提供上百种可视化图形组件,零编码即可实现符合自己业务需求的炫酷可视化大屏。 同时,飞鱼也提供了灵活的拓展能力,支持组件开发、自定义函数与全局事件等配置, 面向复杂需求场景能够保证高效开发与交付。
点击下方地址链接,欢迎大家给 FlyFish 点赞送 Star。参与组件开发,更有万元现金等你来拿。
- GitHub 地址: https://github.com/CloudWise-...
- Gitee 地址:https://gitee.com/CloudWise/f...
- 万元现金活动: http://bbs.aiops.cloudwise.co...
微信扫描识别下方二维码,备注【飞鱼】加入 AIOps 社区飞鱼开发者交流群,与 FlyFish 项目 PMC 面对面交流~
