Assertions, Preconditions, Postconditions and invariants are our allies to avoid invalid objects. Avoiding them leads to hard-to-find errors.
TL;DR: If you turn off your assertions just in production your phone will ring at late hours.
Problems
- Consistency
- Contract breaking
- Hard to debug
- Bad cohesion
Solutions
- Create strong preconditions
- Raise exceptions
- Fail Fast
- Defensive Programming
Examples
Constructors are an excellent first line of defense
Anemic Objects lack these rules.
Sample Code
Wrong
class Date:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
def setMonth(self, month):
self.month = month
startDate = Date(3, 11, 2020)
#OK
startDate = Date(31, 11, 2020)
#Should fail
startDate.setMonth(13)
#Should fail
Right
class Date:
def __init__(self, day, month, year):
if month > 12:
raise Exception("Month should not exceed 12")
#
# etc ...
self._day = day
self._month = month
self._year = year
startDate = Date(3, 11, 2020)
#OK
startDate = Date(31, 11, 2020)
#fails
startDate.setMonth(13)
#fails since invariant makes object immutable
Detection
- It's difficult to find missing preconditions, as long with assertions and invariants.
Tags
- Consistency
Conclusion
Always be explicit on object integrity.
Turn on production assertions.
Even if it brings performance penalties.
Data and object corruption is harder to find.
Fail fast is a blessing.
More info
Relations
Credits
Photo by Jonathan Chng on Unsplash
Writing a class without its contract would be similar to producing an engineering component (electrical circuit, VLSI (Very Large Scale Integration) chip, bridge, engine...) without a spec. No professional engineer would even consider the idea.
Bertrand Meyer
This article is part of the CodeSmell Series.
Last update: 2021/06/23