pragmatic paranoia steven hadfield & anthony rice
TRANSCRIPT
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 two-digit 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.