Generalise edge cases
Generalising edge cases makes code easier to understand as we have a single implementation to reason on. Let's assume we need to implement the following specifications for an airport check-in application:
- Verify a passenger passport has not expired
- If the passenger is a minor, verify that the accompanying parent passport has not expired
class CheckIn(private val today: LocalDate) {
fun checkPassport(passport: Passport): Boolean {
return passport.isValid(today)
}
fun checkMinorPassport(minorPpassport: MinorPassport):Boolean {
return minorPpassport.isValid(today)
}
}
class Passport(private val name: String,
private val lastName: String,
private val expirationDate: LocalDate) {
fun isValid(today: LocalDate): Boolean {
return expirationDate.isBefore(today)
}
}
class MinorPassport(private val name: String,
private val lastName: String,
private val expirationDate: LocalDate,
private val parentPassport: Passport) {
fun isValid(today: LocalDate): Boolean {
return expirationDate.isBefore(today) && parentPassport.isValid(today)
}
}
The above CheckIn
class can be simplified by generalising MinorPassport
to Passport
like follows.
class CheckIn(private val today: LocalDate) {
fun checkPassport(passport: Passport): Boolean {
return passport.isValid(today)
}
}
open class Passport(private val name: String,
private val lastName: String,
private val expirationDate: LocalDate) {
open fun isValid(today: LocalDate): Boolean {
return expirationDate.isBefore(today)
}
}
class MinorPassport(private val name: String,
private val lastName: String,
private val expirationDate: LocalDate,
private val parentPassport: Passport) : Passport(name, lastName, expirationDate) {
override fun isValid(today: LocalDate): Boolean {
// The keyword super is used to invoke the isValid method of Passport
return super.isValid(today) && parentPassport.isValid(today)
}
}
(In Kotlin you need to mark a class or a method with the keyword open
if you want to extend it)
The above code differs from the original one for three aspects:
MinorPassport
extendsPassport
MinorPassport
overrides the methodisValid
ofPassport
, checking the parent passport expiration date tooCheckIn
exposes a singlecheckPassport
method that can be used for both adults and minors passports
Now the CheckIn
class is easier to understand as it only deals with checking a passport. Furthermore, we removed
knowledge duplication in MinorPassport
.
Inheritance is not the only way to generalise edge cases. Another common approach is to use a collection instead of handling multiple scenarios that only differ by the occurrences of something.
Recommended reads
Teach me back
I really appreciate any feedback about the book and my current understanding of software design.