**Design by Contract (DbC)** is a methodology for designing software systems, introduced by Bertrand Meyer in the context of the Eiffel programming language. It emphasizes defining clear contracts between different parts of a software system (e.g., between methods, classes, and modules), specifying precise expectations about inputs, outputs, and behaviors. The core idea is that software components should operate according to well-defined agreements, just as parties in a legal contract.
### Key Concepts of Design by Contract:
1. **Contractual Relationships**:
- In Design by Contract, software components (e.g., classes, methods, functions) engage in a "contractual" relationship. The client (caller) of a method must satisfy certain conditions (pre-conditions), and the method guarantees specific results (post-conditions) once those conditions are met.
- If the contract is violated, the behavior of the method is not guaranteed, similar to breaching a legal contract.
2. **Three Main Elements of a Contract**:
1. **Pre-conditions**:
- These are the conditions that must be true before a method or function is invoked.
- It is the responsibility of the client (caller) to ensure that the pre-conditions are satisfied. If they aren't, the method is not obliged to function correctly.
- Example: In a function that divides two numbers, the pre-condition might be that the divisor must not be zero.
2. **Post-conditions**:
- These are the conditions that must be true after the method or function has executed successfully.
- The method guarantees that the post-conditions will be satisfied as long as the pre-conditions were met.
- Example: After a function sorts an array, the post-condition might be that the array elements are in ascending order.
3. **Invariants**:
- Invariants are conditions that must always hold true for the system or object during its entire lifecycle. They are typically used in object-oriented programming to ensure that an object remains in a valid state throughout its operations.
- Invariants are often class-level constraints that must hold true before and after any public method is executed.
- Example: In a bank account class, an invariant might be that the account balance must never be negative.
### Example:
Suppose you have a method to withdraw money from a bank account:
- **Pre-condition**: The account must have sufficient balance for the withdrawal amount.
- **Post-condition**: The new balance must be equal to the previous balance minus the withdrawal amount.
- **Invariant**: The account balance must always be greater than or equal to zero.
In Python, this could be implemented like so:
```python
class BankAccount:
def __init__(self, balance):
self.balance = balance
assert self.balance >= 0, "Invariant failed: balance must be non-negative" # Invariant
def withdraw(self, amount):
assert amount > 0, "Pre-condition failed: Withdrawal amount must be positive" # Pre-condition
assert self.balance >= amount, "Pre-condition failed: Insufficient balance" # Pre-condition
self.balance -= amount
assert self.balance >= 0, "Post-condition failed: balance cannot be negative" # Post-condition`
```
### Benefits of Design by Contract:
1. **Clear Specifications**:
- DbC provides explicit documentation of what a method expects (pre-conditions) and what it guarantees (post-conditions). This makes it easier for developers to understand the behavior of software components without needing to read the entire implementation.
2. **Improved Software Reliability**:
- By explicitly specifying pre-conditions, post-conditions, and invariants, Design by Contract helps reduce bugs and unexpected behaviors, making software more predictable and reliable.
3. **Error Detection**:
- Contracts act as checks that catch violations early in the development process. If a pre-condition is not satisfied, or a post-condition is violated, the program can raise an error or exception, allowing developers to find and fix bugs more quickly.
4. **Encapsulation of Responsibility**:
- DbC clearly defines the responsibility of each component. The caller is responsible for satisfying the pre-conditions, while the method is responsible for ensuring the post-conditions. This helps isolate problems and make the code more modular.
5. **Modular Reasoning**:
- Contracts make it easier to reason about individual components independently. If a method has a well-defined contract, developers can trust that it will behave correctly as long as the contract is respected, simplifying system complexity.
### Design by Contract vs Defensive Programming:
- **Design by Contract** assumes that clients of a method or function will follow the contract and satisfy pre-conditions. The focus is on specifying and enforcing these contracts rather than defensively handling every possible error or invalid input.
- **Defensive Programming**, on the other hand, assumes that things can go wrong, and a function should always check for and handle invalid inputs or unexpected conditions, even if they violate pre-conditions.
Example:
- In Design by Contract, a method might assert that an input is non-null and raise an error if the pre-condition is violated.
- In Defensive Programming, the method would check if the input is null and handle the situation gracefully (e.g., return a default value or raise a meaningful error).
### Use in Object-Oriented Programming (OOP):
- In object-oriented programming, Design by Contract is often applied at the class level. Classes specify contracts for their methods, ensuring that objects remain in a valid state according to class invariants.
- In DbC, the concept of **inheritance** also comes into play. When a subclass overrides a method, it can weaken the pre-condition (making it less strict) or strengthen the post-condition (making stronger guarantees). This ensures that subclasses remain compatible with the contract of the parent class.
### Design by Contract in Languages:
- **Eiffel**: The Eiffel language natively supports Design by Contract with built-in syntax for specifying pre-conditions, post-conditions, and invariants.
- **Python**: While Python doesn't natively support DbC, you can implement it using assertions, exception handling, or external libraries like `icontract`.
- **C++**: Similar to Python, DbC can be implemented using assertions and custom exception handling.
- **Other Languages**: Many modern programming languages (e.g., Java, C#, etc.) can implement DbC concepts using assertions, testing frameworks, or even custom annotations.
### Example in Eiffel:
```eiffel
class ACCOUNT
feature
balance: INTEGER
withdraw (amount: INTEGER)
require
amount > 0
balance >= amount
do
balance := balance - amount
ensure
balance = old balance - amount
end
invariant
balance >= 0
end
```
### Criticism of Design by Contract:
- **Performance**: Enforcing contracts through checks can add overhead, especially if pre-conditions and post-conditions involve complex checks.
- **Assumption of Correctness**: DbC assumes that clients will always satisfy pre-conditions, which may not hold in practice. In critical systems, defensive programming may be preferred to ensure robustness.
### Conclusion:
**Design by Contract (DbC)** is a powerful methodology that formalizes the expectations between software components, ensuring predictable and reliable behavior. By specifying pre-conditions, post-conditions, and invariants, DbC helps improve the clarity, correctness, and maintainability of software systems. Though not natively supported in many languages, it can be implemented using assertions or additional tools, providing significant benefits in both small- and large-scale software projects.
# References
```dataview
Table title as Title, authors as Authors
where contains(subject, "Design by Contract") or contains(subject, "DbC")
sort title, authors, modified
```