Code Smell 194 - Missing Interval
Tags: webdev, beginners, programming, kotlin
Published: 2023-02-04 17:09:55

*From date should be lower than to date*

> TL;DR: Intervals are there. Why use plain dates?

# Problems

- Missing abstraction

- [Duplicated Code](https://dev.to/mcsee/code-smell-46-repeated-code-4iib)

- Not enforced invariants

- Primitive Obsession

- [Bijection](https://dev.to/mcsee/the-one-and-only-software-design-principle-3086) Fault

- [Fail Fast](https://dev.to/mcsee/fail-fast-48dm) principle violation

# Solutions

1. Create and use an Interval Object

# Context

The restriction "From date should be lower than to date" means that the starting date of a certain interval should occur before the ending date of the same interval.

The "From date" should be a date that comes earlier in time than the "To date". 

This restriction is in place to ensure that the interval being defined makes logical sense and that the dates used to define it are in the correct order.

We all know it. But we miss creating the *Interval* object.

Would you create a *Date* as a pair of 3 *Integer numbers*? Certainly, not. 

This is the same. 

# Sample Code

## Wrong

[Gist Url]: # (https://gist.github.com/mcsee/1e7dafa0143427c7e381017d77da987e)
```kotlin
val from = LocalDate.of(2018, 12, 9)
val to = LocalDate.of(2022, 12, 22)

val elapsed = elapsedDays(from, to)
    
fun elapsedDays(fromDate: LocalDate, toDate: LocalDate): Long {
    return ChronoUnit.DAYS.between(fromDate, toDate)
}

// We need to apply this short function 
// Or the inline version many times in our code
// We don't check from Date to be less than toDate
// We can make accounting numbers with a negative number
```

## Right

[Gist Url]: # (https://gist.github.com/mcsee/cff12c234110259b3b39b6a0122e1b76)
```kotlin
// We reify the Interval Concept

data class Interval(val fromDate: LocalDate, val toDate: LocalDate) {
    init {
        if (fromDate >= toDate) {
            throw IllegalArgumentException("From date must be before to date")
        }
        // Of course the Interval must be immutable
        // By using the keyword 'data'
    }

    fun elapsedDays(): Long {
        return ChronoUnit.DAYS.between(fromDate, toDate)
    }
}

val from = LocalDate.of(2018, 12, 9)
val to = LocalDate.of(2002, 12, 22)

val interval = Interval(from, to) // Invalid

```

# Detection

[X] Manual

This is a primitive obsession smell.

It is related to how we model things.

# Tags

- Primitive

# Conclusion

If you find software with missing simple validations, it certainly needs reification.

# Relations

{% post https://dev.to/mcsee/code-smell-177-missing-small-objects-h2j %}

{% post https://dev.to/mcsee/code-smell-46-repeated-code-4iib %}

{% post https://dev.to/mcsee/code-smell-122-primitive-obsession-4a55 %}

# More Info

{% post https://dev.to/mcsee/fail-fast-48dm %}

# Disclaimer

Code Smells are just my [opinion](https://dev.to/mcsee/i-wrote-more-than-90-articles-on-2021-here-is-what-i-learned-1n3a).

# Credits

Photo by [Towfiqu barbhuiya](https://unsplash.com/@towfiqu999999) on [Unsplash](https://unsplash.com/photos/bwOAixLG0uc)  
  
* * *

> At any particular point in time, the features provided by our programming languages reflect our understanding of software and programming.

_R. E. Fairley_
 
{% post https://dev.to/mcsee/software-engineering-great-quotes-26ci %}

* * *

This article is part of the CodeSmell Series.

{% post https://dev.to/mcsee/how-to-find-the-stinky-parts-of-your-code-1dbc %}