Defensive Design Principles
Introduction to Defensive Programming
Defensive design principles are crucial for creating robust, secure, and reliable Python classes. These principles focus on anticipating potential errors, preventing unexpected behavior, and maintaining code integrity.
Key Defensive Design Strategies
class UserManager:
def create_user(self, username, email, age):
## Comprehensive input validation
if not isinstance(username, str) or len(username) < 3:
raise ValueError("Invalid username")
if not self._validate_email(email):
raise ValueError("Invalid email format")
if not isinstance(age, int) or age < 18:
raise ValueError("User must be 18 or older")
## User creation logic
return self._save_user(username, email, age)
def _validate_email(self, email):
## Implement email validation logic
return '@' in email and '.' in email
2. Exception Handling
class ResourceManager:
def __init__(self, max_resources=10):
self.__resources = []
self.__max_resources = max_resources
def acquire_resource(self, resource):
try:
if len(self.__resources) >= self.__max_resources:
raise RuntimeError("Maximum resources limit reached")
self.__resources.append(resource)
return resource
except Exception as e:
## Centralized error handling
print(f"Resource acquisition failed: {e}")
return None
Defensive Design Flow
flowchart TD
A[Input] --> B{Validate Input}
B --> |Valid| C[Process]
B --> |Invalid| D[Raise Exception]
C --> E{Check Conditions}
E --> |Safe| F[Execute]
E --> |Unsafe| G[Handle Error]
Defensive Design Principles Comparison
| Principle |
Description |
Benefit |
| Input Validation |
Verify input data |
Prevent invalid data |
| Error Handling |
Manage potential exceptions |
Improve system stability |
| Fail-Safe Defaults |
Provide safe fallback options |
Minimize system failures |
| Immutability |
Create unchangeable objects |
Reduce side effects |
Advanced Defensive Coding Pattern
class SecureDataProcessor:
def __init__(self, data_source):
self.__validate_data_source(data_source)
self.__data_source = data_source
def __validate_data_source(self, source):
## Comprehensive source validation
if source is None:
raise ValueError("Data source cannot be None")
if not hasattr(source, 'read'):
raise TypeError("Invalid data source type")
def process_data(self):
try:
## Defensive processing with multiple checks
data = self.__data_source.read()
if not data:
return []
## Additional processing logic
return [item for item in data if self.__is_valid_item(item)]
except Exception as e:
## Centralized error logging
print(f"Processing error: {e}")
return []
def __is_valid_item(self, item):
## Item-level validation
return item is not None and len(str(item)) > 0
Best Practices
- Always validate inputs
- Use type hints and type checking
- Implement comprehensive error handling
- Create fail-safe default behaviors
- Log and monitor potential issues
LabEx Recommendations
LabEx emphasizes that defensive design is not just about preventing errors, but creating resilient and predictable software systems. By implementing these principles, developers can significantly improve code quality and reliability.