Fundamentals 20 min read

Mastering Defensive Programming: Guard Clauses, Exceptions, and DRY in Java

This article explains how to write robust Java code by applying defensive programming techniques such as guard clauses, proper exception handling, validator usage, assertions, and the DRY principle, while illustrating each concept with clear code examples and practical guidelines.

Youku Technology
Youku Technology
Youku Technology
Mastering Defensive Programming: Guard Clauses, Exceptions, and DRY in Java

Guard Clauses (Early Returns)

Replace deep if…else nesting with guard clauses that validate error conditions up‑front and return early. This makes the normal execution path clear and isolates error handling.

public void doSomething(DomainA a) {
    if (a == null) {
        // log errorA
        return;
    }
    if (a.getB() == null) {
        // log errorB
        return;
    }
    if (!(a.getB().getC() instanceof DomainC)) {
        // log errorC
        return;
    }
    // normal flow
    assignAction();
    otherAction();
    doSomethingA();
    doSomethingB();
    doSomethingC();
}

Validator Pattern

Encapsulate parameter validation in a dedicated validator object. The service method calls validate() first; if validation fails a custom unchecked exception is thrown.

public List<DemoResult> demo(DemoParam param) {
    Assert.isTrue(param.validate(), () ->
        new SysException("Parameter validation failed: " + param));
    DemoResult result = doBiz();
    doSomething();
    return result;
}

Example validator implementation using custom annotations:

public class DemoParam extends BaseDO implements ValidateSubject {
    @ValidateString(strMaxLength = 128)
    private String aString;

    @ValidateObject(require = true)
    private List<SubjectDO> bList;

    @ValidateString(require = true, strMaxLength = 128)
    private String cString;

    @ValidateLong(require = true)
    private Long dLong;

    @Override
    public boolean validate0(ValidateSubject subject) throws ValidateException {
        if (subject instanceof DemoParam) {
            DemoParam p = (DemoParam) subject;
            return StringUtils.isNotBlank(p.getAString()) &&
                   SubjectDO.allValidate(p.getBList());
        }
        return false;
    }
}

Assertions

Use assertions only to document impossible states during development. They must have no side effects because they can be disabled at runtime. A Spring‑based utility can provide unchecked‑exception‑based assertions that remain active in production.

public class Assert extends org.springframework.util.Assert {
    public static <T extends RuntimeException> void isTrue(boolean expr, Supplier<T> supplier) {
        if (!expr) {
            if (supplier != null) {
                throw supplier.get();
            }
            throw new IllegalArgumentException();
        }
    }
}

Never place essential logic inside a Java assert statement.

Exception‑Handling Best Practices

Use exceptions only for truly exceptional situations; do not employ them for normal control flow (e.g., catching ArrayIndexOutOfBoundsException to terminate a loop).

Prefer explicit checks or guard clauses for expected error conditions.

Distinguish between checked and unchecked exceptions:

Checked exceptions (e.g., FileNotFoundException) signal recoverable conditions; callers are expected to handle or propagate them.

Unchecked exceptions (runtime exceptions and errors) indicate programming bugs and should generally be allowed to propagate to a top‑level handler.

Example of proper use of a checked exception:

public static void openPasswd() throws FileNotFoundException {
    FileInputStream fs = new FileInputStream("/etc/passwd");
}

Example of defensive handling for a user‑provided file:

public static boolean openUserFile(String path) throws FileNotFoundException {
    File f = new File(path);
    if (!f.exists()) {
        return false; // caller can decide how to react
    }
    FileInputStream fs = new FileInputStream(path);
    return true;
}

DRY Principle and Rule of Three

The DRY (Don’t Repeat Yourself) principle states that each piece of knowledge should have a single, authoritative representation. It discourages duplicated logic, not merely duplicated lines.

When a piece of functionality appears only once or twice, premature abstraction adds maintenance overhead. The “Rule of Three” advises:

First occurrence – write straightforward code.

Second occurrence – copy the code.

Third occurrence – extract a reusable abstraction.

This balances the benefits of DRY against the risk of over‑engineering.

Summary of Defensive Programming Practices

Apply guard clauses to validate inputs early and exit on error.

Encapsulate validation logic in validator objects with clear contracts.

Use assertions for development‑time sanity checks; replace them with runtime‑safe checks in production.

Reserve exceptions for truly exceptional conditions; prefer explicit checks for expected errors.

Distinguish checked (recoverable) from unchecked (programming errors) exceptions.

Apply DRY judiciously and follow the Rule of Three to avoid premature abstraction.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaException Handlingcode qualityguard-clausesDRY principledefensive programming
Youku Technology
Written by

Youku Technology

Discover top-tier entertainment technology here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.