By Steven Feuerstein
Price: $29.99 USD
£18.50 GBP
Cover | Table of Contents | Colophon
Well, every 50 miles or so, one of my wheels might fall off. The odometer sometimes goes backward, and, oh look! My windows don't quite close. But that's all right, because the dealer said that I'll be getting version 2 in roughly six months. I can't wait!
The excuse_in_use Boolean function you wrote sometimes returns null and that makes it a pain to use. I always have to wrap calls to it with NVL and decide—yet again—that NULL=FALSE. Having to worry about null values in computations can really cause trouble in the form of hard-to-catch bugs. Can't excuse_in_use just return TRUE or FALSE, period?
Well, it wouldn't be such a problem if callers like your customer_support package could be counted on not to pass null excuses into the function. But how am I supposed to know what you want when you hand me a null excuse to test? Since we've never discussed it, I figure the best policy for null is to hand out what is handed in, which is to say exactly no information. The program is more robust because it handles more input combinations without failing. It's called defensive programming.
You are both arguing plenty, but communicating very little. Delaware, defensive programming is a fine approach when the potential callers of a module are unknown or otherwise unpredictable, but Lizbeth sits in the cube right next to you! Maybe we should defend less and cooperate more by coming up with some clear agreements about how our code should behave.
PACKAGE BODY excuse_tracker
IS
TYPE used_aat IS TABLE OF BOOLEAN INDEX BY excuse_excuse_t;
g_excuses_used used_aat;
FUNCTION excuse_in_use (excuse_in IN excuse_excuse_t)
RETURN BOOLEAN
IS
BEGIN
IF excuse_in IS NULL
THEN
RETURN null;
ELSE
RETURN g_excuses_used.EXISTS (excuse_in);
END IF;
END excuse_in_use;
...other programs...
END excuse_tracker;
In this case, Lizbeth does not want Delaware's excuse_in_use function to ever return a null value instead of a proper Boolean, and similarly, Delaware does not want Lizbeth's program to call his function with a null value for its input argument. So let's just satisfy both of them by requiring Delaware's function to only return TRUE or FALSE, but only under the obligation that Lizbeth's code does not pass in a null value. In fact, this is a very good rule in general for all Boolean functions, and we should probably always follow it.
FUNCTION excuse_for (id_in IN mfe_excuses.id%TYPE)
RETURN mfe_excuses%ROWTYPE
IS
retval mfe_excuses%ROWTYPE;
BEGIN
SELECT * INTO retval
FROM mfe_excuses
WHERE id = id_in;
RETURN retval;
END excuse_for;
FUNCTION customer_for (id_in IN mfe_customers.id%TYPE)
RETURN mfe_customers%ROWTYPE
IS
retval mfe_customers%ROWTYPE;
BEGIN
SELECT * INTO retval
FROM mfe_customers
WHERE id = id_in;
RETURN retval;
END excuse_for;
CREATE OR REPLACE PACKAGE OVERDUE_PKG IS PROCEDURE SET_DAILY_FINE (FINE_IN IN NUMBER); FUNCTION DAILY_FINE RETURN NUMBER; FUNCTION DAYS_OVERDUE (ISBN_IN IN BOOK.ISBN%TYPE)RETURN INTEGER; FUNCTION FINE_AMOUNT (ISBN_IN IN BOOK.ISBN%TYPE) RETURN INTEGER; END OVERDUE_PKG;
PROCEDURE showBorrowedBooks (date_borrowed IN DATE)
IS
date_returned CONSTANT DATE := SYSDATE;
minDaysBorrowed PLS_NTEGER := 10;
TYPE bookBorrowed IS RECORD (
dateBorrowed DATE,
daysBorrowed PLS_INTEGER,
isbn book.isbn%TYPE,
dateDue DATE);
bb bookBorrowed;
CURSOR c IS
SELECT * FROM borrowed_book
WHERE returned = 'N';
BEGIN
IF dateborrowed < date_returned
THEN
FOR rec IN c
LOOP
bb:= rec;
IF bb.daysBorrowed > minDaysBorrowed
THEN
DBMS_OUTPUT.PUT_LINE (bb.isbn);
END IF;
END LOOP;
END IF;
END showBorrowedBooks;
PACKAGE misc_stuff IS FUNCTION calculate_totals (...) RETURN NUMBER; PROCEDURE favorite_excuses (...); END misc_stuff;
BEGIN
IF misc_stuff.calculate_totals (...) > 10000
THEN
misc_stuff.favorite_excuses (...);
END IF;
END;
PACKAGE misc_stuff IS FUNCTION total_excuse_revenue (...) RETURN NUMBER; PROCEDURE show_favorite_excuses (...); END misc_stuff;
BEGIN
IF misc_stuff.total_excuse_revenue (...) > 10000
THEN
show_favorite_excuses (...);
END IF;
END;
IF master_list(l_curr_index).properties_flag.field1 = 'N' THEN
/* If the first field of the properties record is N... */ IF master_list(l_curr_index).properties_flag.field1 = 'N' THEN
/* If the customer is not eligible for a discount... */ IF master_list(l_curr_index).properties_flag.field1 = 'N' THEN