This is an old revision of the document!
Table of Contents
Invariant Avoidance Principle
Variants and Alternative Names
Context
Principle Statement
Avoid Invariants and Preconditions.
Description
Rationale
Strategies
- If the language supports that, use references which cannot be
null
- In C++ use references instead of pointers (see example 3: C++ references)
- In Java use primitive types instead of their object wrappers (
int
instead ofInteger
but notint
instead ofCustomer
)
- Use value objects instead of primitive types (see example 2: string preconditions)
- Avoid duplication of information. If the same information is stored in different places (maybe in different formats), the values may get out of sync (see also DRY). This also applies to caching.
Caveats
Keep in mind that preconditions and invariants are absolutely necessary for every software. So this principle is constantly violated. Introducing preconditions and invariants is often also done deliberately in order to simplify the code (see KISS). So the purpose of this principle is mainly to point out that there are drawbacks. By no means invariants are problematic themselves or should be entirely avoided. They just also have disadvantages.
See also section contrary principles.
Origin
This principle is newly introduced here.
Evidence
Relations to Other Principles
Generalizations
- Murphy's Law (ML): ML states that an invariant will eventually be broken. So IAP is the application of ML to invariants.
Specializations
Contrary Principles
- Keep It Simple Stupid (KISS): Adding an invariant typically makes the code easier, as it can be assumed that the invariant holds. In fact that is often the very purpose if introducing invariants: Either they make the design easier or they are inevitable. Otherwise they should be avoided.
Complementary Principles
- Information Hiding/Encapsulation (IH/E): When an invariant cannot be avoided, it should at least be encapsulated.
- Liskov Substitution Principle (LSP): Invariants can also be broken by subtypes. LSP tells that invariants may only be strengthened by subtypes, so they are not broken.
- Fail Fast (FF): Breaking an invariant is a defect. And in such a case the software should fail fast.
- Don't Repeat Yourself (DRY): Duplication of information, like having the same data in different representations or like caching values, creates invariants. So an invariant sometimes is a hidden DRY violation.
Principle Collections
Examples
Example 1: Index Preconditions
public void prettyPrintItem(List<Item> items, int index) { ... }
This method has the following preconditions:
items
may not benull
index
must be greater or equal0
index
must be lesser thanitems.size()
Compare the following solution:
public void prettyPrintItem(Item item) { ... }
This is better as it just has one precondition: item
may not be null
Example 2: String Preconditions
public void downloadFile(String url) { ... }
This method has the following preconditions:
url
may not benull
url
must contain a valid URL (which is even a quite complicated precondition)
Compare the following method:
public void downloadFile(URL url) { ... }
This is better since there is only one precondition: url
may not be null
Example 3: C++ References
Sompare the following two methods:
void prettyPrint(SomeClass * obj) { ... }
void prettyPrint(SomeClass& obj) { ... }
In the second version obj
cannot be NULL
as it is a reference and not a pointer. So there is one precondition less.
Example 4: DRY
A class for complex numbers should either store the real and the imaginary part or absolute value and argument but not both. The values which are not stored can be computed in the getter method.