《Design by Contract for Embedded Software》 翻译( 六 )


Defensive or Preemptive Programming?防御式还是进攻式编程?
The term “defensive programming” seems to have two complementary meanings. In the first meaning, the term is used to describe a programming style based on assertions, where you explicitly assert any assumptions that should hold true as long as the software operates correctly.3 In this sense, “defensive programming” is essentially synonymous with DbC.

术语 "防御性编程 "似乎有两个互补的含义 。在第一种含义中,这个术语被用来描述一种基于断言的编程风格,在这种风格中,你明确断言任何假设,只要软件运行正常,这些假设就应该是真实的 。在这个意义上,"防御性编程 "基本上是 DbC 的同义词 。
In the other meaning, however, “defensive programming” denotes a programming style that aims at making operations more robust to errors, by accepting a wider range of inputs or allowing an order of operations not necessarily consistent with the object’s state. In this sense, “defensive programming” is complementary to DbC. For example, consider the following hypothetical output Port class:
然而,在另一种意义上,"防御性编程 "指的是一种编程风格,旨在通过接受更广泛的输入或允许不一定与对象的状态一致的操作顺序,使操作对错误更加稳健 。在这个意义上,"防御性编程 "是对 DbC 的补充 。例如,考虑下面这个假想的输出端口类:
class Port {bool open_;public:Port() : open_(false) {}void open() {if (!open_) {// open the port ...open_ = true;}}void transmit(unsigned char const *buffer, unsigned nBytes) {if (open_ && buffer != NULL && nBytes > 0) {// transmit nBytes// from the buffer ...}}void close() {if (!open_) {open_ = false;// close the port ...}}// . . .};This class is programmed defensively (in the second meaning of the term), because it silently accepts invoking operations out of order (that is, transmit() before open()) with invalid parameters (e.g., transmit(NULL, 0)). This technique of making operations more robust to errors is often advertised as a better coding style, but unfortunately, it often hides bugs. Is it really a good program that calls port.transmit() before port.open()? Is it really OK to invoke transmit() with an uninitialized transmit buffer? I’d argue that a correctly designed and implemented code should not do such things, and when it happens it’s a sure indication of a larger problem. In comparison, the Port class coded according to the DbC philosophy would use preconditions:
这个类的编程是防御性的(在这个术语的第二个含义中),因为它默默地接受以无效的参数(例如,transmit (NULL, 0))不按顺序调用操作(即在 open () 之前调用 transmit ()) 。这种使操作对错误更健壮的技术经常被宣传为更好的编码风格,但不幸的是,它经常隐藏着错误 。在 port. open () 之前调用 port. transmit () 真的是一个好程序吗?用一个未初始化的发送缓冲区调用发送 () 真的可以吗?我认为,一个正确设计和实现的代码不应该做这样的事情,当它发生时,肯定是一个更大的问题的迹象 。相比之下,根据 DbC 理念编码的 Port 类会使用前置条件:
class Port {bool open_;public:Port() : open_(false) {}void open() {REQUIRE(!open_);// open the port ...open = true;}void transmit(unsigned char const *buffer, unsigned nBytes) {REQUIRE(open_ && buffer != NULL && nBytes > 0);// transmit n-bytes// from the buffer ...}void close() {REQUIRE(open_);open_ = false;// close the port ...}// . . .};This implementation is intentionally less flexible, but unlike the defensive version, it’s hard to use this one incorrectly. Additionally (although difficult to convincingly demonstrate with a toy example like this), assertions tend to eliminate a lot of code that you would have to invent to handle the wider range of inputs allowed in the defensive code.

经验总结扩展阅读