PRAGMATIC PARANOIA Steven Hadfield & Anthony Rice You can’t write perfect software Accept it Nobody else writes it either Be defensive about other people’s code Play it safe Design by Contract Developed by Bertrand Meyer (Eiffel) Use contracts to force both parties into an agreement: caller and routine Each party is expected to uphold their end of the deal Preconditions, postconditions, and class invariants Preconditions What the caller guarantees will be passed to the routine The routine’s requirements It’s up to the calling code to make sure that the requirements are kept Postconditions What the routine guarantees to return How the world should be after the routine is done Lazy code: require a lot, promise little in return Class invariants Conditions that should always be true from caller’s perspective Routine possibly allowed to change it temporarily while working, but should return to previous state Things that the routine is not allowed to change Applies to all methods in a class Designing with Contracts It’s a design technique Directly supported by some languages Handy if compiler can do it, but not necessary Assertions – partially emulate DBC Use preprocessors for languages without Messy and not as good, but helpful Can be dynamically generated Rejected and/or negotiated Invariants Also applies at lower levels Loop invariant Making sure something is true before and during a loop Semantic invariants Central to the purpose of a task Should be clear and unambiguous Dead Programs Tell No Lies If a program is going to crash, do it early Crash with class Provide useful information If the impossible happens, die as soon as possible Everything after the impossible happens is suspect Assertive Programming This can never happen… “This code will not be used 30 years from now, so twodigit dates are fine” “This application will never be used abroad, so why internationalize it?” “count can’t be negative” “This printf can’t fail” Assertions If it can’t happen, use assertions to ensure that it won’t Don’t use assertions as error handling, they should just be used to check for things that should never happen. Void writeString(char *string){ assert(string != NULL); Never put code that should be executed into as assert. Leave Assertions Turned On Misunderstanding: Since they check for things that should never happen, the are only triggered by a bug in the code. They should be turned off when shipped to make the code run faster. Leave Assertions Turned On You cannot assume that testing will find all the bugs. Your program runs in a dangerous world. Your first line of defense is checking for any possible error, and your second is using assertions to try to detect those you missed. Assertions and Side Effects Instead of: While (iter.hasMoreElements()) { Test.ASSERT(iter.nextElement() != null); Object obj = iter.nextElement(); Do: While (iter.hasMoreElements()) { Object obj = iter.nextElement(); Test. ASSERT(obj != null); When to Use Exceptions Checking for every possible error can lead to some pretty ugly code. If the programming language supports exceptions, you can use try catch loops to make the code much easier to read. What is Exceptional? Exceptions should be reserved for unexpected events. They should rarely be used as the programs normal flow. Use Exceptions for Exceptional Programs An exception represents an immediate, nonlocal transfer of control. Using exceptions as part of normal processing will give you all the readability and maintainability problems of spaghetti code. Error Handlers Are an Alternative Error Handlers are routines that are called when an error is detected. These routines can handle a specific category of errors. How to Balance Resources Finish What You Start Many developers have not consistent plan for dealing with resource allocation and deallocation. The routine or object that allocates a resource should be responsible for deallocating it. Nest Allocations The basic pattern for resource allocation can be extended for routines that need more than one resource at a time. Deallocate resources in the opposite order in which you allocate them. When allocating the same set of resources in different places in your code, always allocate them in the same order. Objects and Exceptions Encapsulation of resources in classes Instantiate that class when you need a particular resource type. When You Can’t Balance Resources Commonly found in programs that use dynamic data structures. When you deallocate the top-level data structure: Top-level structure responsible for substructures. Top-level structure is simply deallocated. Top-level structure refuses to deallocate itself if it contains substructures. Choice depends on individual data structure. Checking the Balance Produce wrappers for each resource, and keep track of all allocations and deallocations. When program logic says resources will be in a certain state you can use the wrappers to check it.