In-Depth Notes on Programming by Contract and Error Management
Objectives
- Common Programming Errors: Understand the types of errors encountered in programming.
- Programming by Contract: Define this concept and its implications for software development.
- Precondition vs Postcondition & Class Invariants: Differentiate these vital components in contract-based programming.
Writing Robust Code
- Classes of Problems in Running Programs:
- System Failures: CPU failures, memory errors, full disk, network crash, etc.
- Invalid Data: Errors due to user input or bad data (e.g., out of range).
- Programming Errors: Lead to unknown or invalid states in the software.
Assertions and Exceptions
- Assertions:
- Manually added to enhance robustness in code.
- Used as part of the programming contract to validate conditions.
Dependability and Object-Orientation
- Object-Oriented Design:
- Quality and dependability are critical for module reusability.
- Design by contract principles can be applied across different object-oriented languages to ensure robustness in software.
Understanding Contracts
- Parties Involved:
- Client: Requests a service.
- Supplier: Provides the service.
- Contract Characteristics:
- Each party has expectations of benefits and obligations.
- Obligations define what is to be done for benefits to be granted.
Contract Document
- Clarifies responsibilities and expectations:
- Protects client by specifying service results.
- Protects supplier from non-specified obligations.
- No Hidden Clauses: Only documented obligations apply.
How Contracts and Software Design Interact
- Contractual Analogy in Software:
- Pre and post conditions act as obligations and benefits in code.
- Encourages clear definitions of expected conditions before and after a function execution.
Precondition and Postcondition
- Precondition: Requirements that must be met before a function is called.
- Postcondition: Conditions guaranteed to be true after executing the function.
Examples:
- Square Root Function:
void write_sqrt(double x) {
// Precondition: x >= 0.
// Postcondition: The square root of x is printed.
}
- Vowel Checking Function:
bool is_vowel(char letter) {
// Precondition: letter is a letter (A-Z or a-z).
// Postcondition: Returns true if letter is a vowel, false otherwise.
}
- Division Function:
int divide(int a, int b) {
// Precondition: b != 0;
// Postcondition: Returns a / b.
}
- Note: Violating the precondition (e.g., dividing by zero) breaks the contract.
Defensive Programming vs Design by Contract
- Defensive Programming:
- Checks for unexpected conditions everywhere.
- Leads to complex and redundant checks.
- Design by Contract:
- Clearly defined responsibilities reduce redundancy.
- Encourages simpler and more maintainable codes.
Sample Class: BankAccount
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
assert initialBalance >= 0 : "Initial balance must be non-negative";
this.balance = initialBalance;
checkInvariant();
}
public void deposit(double amount) {
assert amount > 0 : "Deposit amount must be positive";
double oldBalance = balance;
balance += amount;
assert balance == oldBalance + amount : "Balance must increase by deposit amount";
checkInvariant();
}
public void withdraw(double amount) {
assert amount > 0 : "Withdraw amount must be positive";
assert amount <= balance : "Cannot withdraw more than the balance";
double oldBalance = balance;
balance -= amount;
assert balance == oldBalance - amount : "Balance must decrease by withdrawal amount";
checkInvariant();
}
public double getBalance() {
return balance;
}
private void checkInvariant() {
assert balance >= 0 : "Invariant violated: balance is negative";
}
}
Main Method Example:
public static void main(String[] args) {
BankAccount account = new BankAccount(100.0);
account.deposit(50.0);
account.withdraw(30.0);
System.out.println("Final balance: " + account.getBalance());
}
Lab Activities and Exercises
- Square Root Writer: Create a method to print the square root of a non-negative number.
- Vowel Checker: Check for vowel characters (case-insensitive).
- Safe Division: Implement division with non-zero denominator check.
- Evaluate the purpose of the assert keyword in Java and the consequences of unmet preconditions.
- Discuss the benefits of contracts in larger software projects for team functionality.