Software Engineering for Self-Directed Learners »

Implementation → Error Handling →

Defensive programming

What

Can explain defensive programming

A defensive programmer codes under the assumption "if you leave room for things to go wrong, they will go wrong". Therefore, a defensive programmer proactively tries to eliminate any room for things to go wrong.

Consider a method MainApp#getConfig() that returns a Config object containing configuration data. A typical implementation is given below:

class MainApp {
    Config config;
    
    /** Returns the config object */
    Config getConfig() {
        return config;
    }
}

If the returned Config object is not meant to be modified, a defensive programmer might use a more defensive implementation given below. This is more defensive because even if the returned Config object is modified (although it is not meant to be), it will not affect the config object inside the MainApp object.

    /** Returns a copy of the config object */
    Config getConfig() {
        return config.copy(); // return a defensive copy
    }

Enforcing compulsory associations

Can use defensive coding to enforce compulsory associations

Consider two classes, Account and Guarantor, with an association as shown in the following diagram:

Example:

Here, the association is compulsory i.e. an Account object should always be linked to a Guarantor. One way to implement this is to simply use a reference variable, like this:

class Account {
    Guarantor guarantor;

    void setGuarantor(Guarantor g) {
        guarantor = g;
    }
}

However, what if someone else used the Account class like this?

Account a = new Account();
a.setGuarantor(null);

This results in an Account without a Guarantor! In a real banking system, this could have serious consequences! The code here did not try to prevent such a thing from happening. You can make the code more defensive by proactively enforcing the multiplicity constraint, like this:

class Account {
    private Guarantor guarantor;

    public Account(Guarantor g) {
        if (g == null) {
            stopSystemWithMessage(
                    "multiplicity violated. Null Guarantor");
        }
        guarantor = g;
    }
    public void setGuarantor(Guarantor g) {
        if (g == null) {
            stopSystemWithMessage(
                    "multiplicity violated. Null Guarantor");
        }
        guarantor = g;
    }
    // ...
}

Exercises



Enforcing 1-to-1 associations

Can use defensive coding to enforce 1-to-1 associations

Consider the association given below. A defensive implementation requires us to ensure that a MinedCell cannot exist without a Mine and vice versa which requires simultaneous object creation. However, Java can only create one object at a time. Given below are two alternative implementations, both of which violate the multiplicity for a short period of time.

Option 1:

class MinedCell {
    private Mine mine;

    public MinedCell(Mine m) {
        if (m == null) {
            showError();
        }
        mine = m;
    }
    // ...
}

Option 1 forces us to keep a Mine without a MinedCell (until the MinedCell is created).

Option 2:

class MinedCell {
    private Mine mine;

    public MinedCell() {
        mine = new Mine();
    }
    // ...
}

Option 2 is more defensive because the Mine is immediately linked to a MinedCell.


Enforcing referential integrity

Can use defensive coding to enforce referential integrity of bidirectional associations

A bidirectional association in the design (shown in (a)) is usually emulated at code level using two variables (as shown in (b)).

class Man {
    Woman girlfriend;

    void setGirlfriend(Woman w) {
        girlfriend = w;
    }
    // ...
}
class Woman {
    Man boyfriend;

    void setBoyfriend(Man m) {
        boyfriend = m;
    }
}

The two classes are meant to be used as follows:

Woman jean;
Man james;
// ...
james.setGirlfriend(jean);
jean.setBoyfriend(james);

Suppose the two classes were used like this instead:

Woman jean;
Man james, yong;
// ...
james.setGirlfriend(jean);
jean.setBoyfriend(yong);

Now James' girlfriend is Jean, while Jean's boyfriend is not James. This situation is a result of the code not being defensive enough to stop this "love triangle". In such a situation, you could say that the referential integrity has been violated. This means that there is an inconsistency in object references.

One way to prevent this situation is to implement the two classes as shown below. Note how the referential integrity is maintained.

public class Woman {
    private Man boyfriend;

    public void setBoyfriend(Man m) {
        if (boyfriend == m) {
            return;
        }
        if (boyfriend != null) {
            boyfriend.breakUp();
        }
        boyfriend = m;
        m.setGirlfriend(this);
    }

    public void breakUp() {
        boyfriend = null;
    }
    // ...
}
public class Man {
    private Woman girlfriend;

    public void setGirlfriend(Woman w) {
        if (girlfriend == w) {
            return;
        }
        if (girlfriend != null) {
            girlfriend.breakUp();
        }
        girlfriend = w;
        w.setBoyfriend(this);
    }
    public void breakUp() {
        girlfriend = null;
    }
    // ...
}

When james.setGirlfriend(jean) is executed, the code ensures that james breaks up with any current girlfriend before he accepts jean as his girlfriend. Furthermore, the code ensures that jean breaks up with any existing boyfriends before accepting james as her boyfriend.

Exercises



When

Can explain when to use defensive programming

It is not necessary to be 100% defensive all the time. While defensive code may be less prone to be misused or abused, such code can also be more complicated and slower to run.

The suitable degree of defensiveness depends on many factors such as:

  • How critical is the system?
  • Will the code be used by programmers other than the author?
  • The level of programming language support for defensive programming
  • The overhead of being defensive

Exercises