Defensive Programming: Concepts, Core Principles, and Practical Cases
This article introduces defensive programming, explains its fundamental concepts and core principles such as risk identification and input validation, and demonstrates practical applications through multiple Java code examples covering pagination, loop prevention, exception handling, boundary checks, and assertions to improve software robustness.
Defensive programming is a software development paradigm that anticipates and mitigates potential errors and exceptional conditions, aiming to keep applications stable in complex and unpredictable environments.
Basic Concept : It emphasizes proactive error prevention by considering possible faults during design and implementation, thereby reducing bugs and failures.
Core Principles include:
Risk identification – distinguishing non‑systemic risks (e.g., null pointers) from systemic risks (e.g., infinite loops).
Defensive rules – assume inputs are erroneous, minimize error impact, use assertions, write clear code, and continuously test.
Case 1 – Input Validation & Cleaning : For a web form, validate data types, length, range, and sanitize characters before processing.
Example (Java pagination service):
public class PaginationService {
private static final int MAX_PAGE_SIZE = 100;
/**
* Get pagination info with parameter validation
* @param totalRecords total number of records
* @param pageSize records per page
* @param pageNumber current page number
* @return PaginationInfo containing total pages, current page, etc.
*/
public PaginationInfo getPaginationInfo(int totalRecords, int pageSize, int pageNumber) {
// Validate pageSize
if (pageSize <= 0 || pageSize > MAX_PAGE_SIZE) {
throw new IllegalArgumentException("pageSize must be a positive integer not exceeding " + MAX_PAGE_SIZE);
}
// Validate pageNumber
if (pageNumber <= 0) {
pageNumber = 1; // default to first page
}
int totalPages = (totalRecords + pageSize - 1) / pageSize;
if (pageNumber > totalPages) {
pageNumber = totalPages;
}
int startIndex = (pageNumber - 1) * pageSize;
return new PaginationInfo(totalPages, pageNumber, startIndex);
}
// PaginationInfo is a simple DTO
...
}This method first validates pageSize and pageNumber , throws an exception for illegal values, computes total pages, adjusts the page number if necessary, and returns a structured result.
Case 2 – Preventing Infinite Loops : Ensure loop exit conditions are reachable and validate loop parameters.
public List
generateList(int startMinutes, int endMinutes, int interval, int duration) {
if (interval <= 0) {
throw new IllegalArgumentException("Invalid parameters: interval must be positive integers.");
}
List
result = new ArrayList<>();
int nextStartTime = startMinutes;
while (nextStartTime <= endMinutes) { // avoid equality‑only condition
int currentStart = nextStartTime;
int currentEnd = currentStart + duration;
result.add(currentStart + "-" + currentEnd);
nextStartTime += interval;
}
return result;
}Case 3 – Exception Handling : Wrap risky operations in try‑catch blocks, differentiate exception types, and log detailed error information.
public static String readFile(String filePath) {
try {
byte[] encoded = Files.readAllBytes(Paths.get(filePath));
return new String(encoded);
} catch (FileNotFoundException e) {
log.info("File not found: " + filePath);
return null;
} catch (Exception e) {
log.info("Error reading file: " + e.getMessage());
return null;
}
}Case 4 – Boundary Condition Checks : Verify array indices and collection accesses before use.
public class ArrayAccess {
public static void main(String[] args) {
int[] numbers = {1,2,3,4,5};
int index = getIndexFromUser(); // assume valid input
if (index >= 0 && index < numbers.length) {
log.info(numbers[index]);
} else {
log.info("Index out of bounds");
}
}
private static int getIndexFromUser() {
return 2; // example value
}
}Case 5 – Using Assertions : Apply assertions to verify internal invariants during development.
public static int calculateAge(int birthYear) {
if (birthYear <= 0 || birthYear > java.time.Year.now().getValue()) {
throw new IllegalArgumentException("Birth year must be a positive integer less than the current year");
}
int currentYear = java.time.Year.now().getValue();
return currentYear - birthYear;
}
public static void main(String[] args) {
try {
int age = calculateAge(1990);
log.info("Age is: " + age);
} catch (IllegalArgumentException e) {
log.info("Error: " + e.getMessage());
}
}Challenges : Over‑defending can bloat code and degrade performance; therefore, defensive measures should be applied judiciously, focusing on entry points (e.g., input validation) and critical loops.
Conclusion : Defensive programming is a proactive strategy that improves software quality and stability by foreseeing and preventing errors, making systems more resilient to unexpected inputs and runtime anomalies.
JD Tech Talk
Official JD Tech public account delivering best practices and technology innovation.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.