avatarEmanuel Trandafir

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

8785

Abstract

test</span><span class="hljs-params">()</span> { <span class="hljs-type">var</span> <span class="hljs-variable">people</span> <span class="hljs-operator">=</span> List.of( <span class="hljs-keyword">new</span> <span class="hljs-title class_">GeolocatedPerson</span>(<span class="hljs-string">"Alice"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Location</span>(<span class="hljs-string">"New York"</span>, <span class="hljs-number">40.7128</span>, -<span class="hljs-number">74.0060</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">GeolocatedPerson</span>(<span class="hljs-string">"Bob"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Location</span>(<span class="hljs-string">"London"</span>, <span class="hljs-number">51.5074</span>, -<span class="hljs-number">0.1278</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">GeolocatedPerson</span>(<span class="hljs-string">"Charlie"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">Location</span>(<span class="hljs-string">"Paris"</span>, <span class="hljs-number">48.8566</span>, <span class="hljs-number">2.3522</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">UnlocatedPerson</span>(<span class="hljs-string">"Tony"</span>) );

<span class="hljs-type">var</span> <span class="hljs-variable">rio</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Location</span>(<span class="hljs-string">"Rio de Janeiro"</span>, -<span class="hljs-number">22.9068</span>, -<span class="hljs-number">43.1729</span>);
people.stream()
    .map(pers -&gt; pers.distanceTo(rio))
    .forEach(dist -&gt; System.out.println(dist));

}</pre></div><h2 id="b47e">The “Monadic” Location</h2><p id="f6f0">Alternatively, we can encapsulate this logic inside the <i>Location </i>object. To do so, we’ll use an (almost) monadic approach. In other words, the <i>Person </i>will always have a <i>Location</i>, but the location itself can be either known or unknown.</p><p id="7402">For expressing these two possible states of the <i>Location </i>without keeping a <i>null</i> or <i>Optional </i>reference, we’ll use, yet again, polymorphism:</p><div id="06a0"><pre><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Location</span> { String <span class="hljs-title function_">name</span><span class="hljs-params">()</span>; }

<span class="hljs-keyword">record</span> <span class="hljs-title class_">KnownLocation</span><span class="hljs-params">( <span class="hljs-type">double</span> longitude, <span class="hljs-type">double</span> latitude )</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">name</span><span class="hljs-params">()</span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"(%s, %s)"</span>.formatted(longitude, latitude); } }

<span class="hljs-keyword">record</span> <span class="hljs-title class_">UnknownLocation</span><span class="hljs-params">()</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">name</span><span class="hljs-params">()</span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"Null Island"</span>; } }</pre></div><p id="3241">For convenience, we can add some static factory methods in the public interface:</p><div id="c52c"><pre><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Location</span> { String <span class="hljs-title function_">name</span><span class="hljs-params">()</span>;

<span class="hljs-keyword">static</span> Location <span class="hljs-title function_">of</span><span class="hljs-params">(<span class="hljs-type">double</span> longitude, <span class="hljs-type">double</span> latitude)</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">KnownLocation</span>(longitude, latitude);
}

<span class="hljs-keyword">static</span> Location <span class="hljs-title function_">unknown</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UnknownLocation</span>();
}

}</pre></div><p id="6c10">Lastly, we need to add the method for calculating the distance<i>. </i>If we add it to the common interface, the <i>UnknownLocation </i>will throw an exception while implementing it: this would be a clear violation of <i>Liskov’s Substitution Principle</i> and a bad practice overall. Therefore, we’ll only add this method inside the <i>KnownLocation </i>class:</p><div id="efbf"><pre><span class="hljs-keyword">record</span> <span class="hljs-title class_">KnownLocation</span><span class="hljs-params">(<span class="hljs-type">double</span> longitude, <span class="hljs-type">double</span> latitude)</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">name</span><span class="hljs-params">()</span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"(%s, %s)"</span>.formatted(longitude, latitude); }

<span class="hljs-keyword">public</span> <span class="hljs-type">double</span> <span class="hljs-title function_">distanceTo</span><span class="hljs-params">(KnownLocation other)</span> { 
    retrun ...; <span class="hljs-comment">// the calculation goes here</span>
}

}</pre></div><p id="6684">Now that we have this functionality, the next question is how to make the most of it. One potential approach is to adopt a strategy similar to that used by the Optional API. For instance, we can implement methods like <i>ifKnown</i> or <i>ifKnownOrElse</i> to help us leverage the available data in a convenient and effective way:</p><div id="517b"><pre><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Location</span> { <span class="hljs-title class_">String</span> <span class="hljs-title function_">name</span>();

<span class="hljs-keyword">static</span> <span class="hljs-title class_">Location</span> <span class="hljs-title function_">of</span>(<span class="hljs-params">double longitude, double latitude</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">KnownLocation</span>(longitude, latitude);
}

<span class="hljs-keyword">static</span> <span class="hljs-title class_">Location</span> <span class="hljs-title function_">unknown</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UnknownLocation</span>();
} 

<span class="hljs-built_in">void</span> <span class="hljs-title function_">ifKnownOrElse</span>(<span class="hljs-title class_">Consumer</span>&lt;<span class="hljs-title class_">KnownLocation</span>&gt; action, <span class="hljs-title class_">Runnable</span> orElse);

}

record <span class="hljs-title class_">KnownLocation</span>( double longitude, double latitude ) <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-title class_">String</span> <span class="hljs-title function_">name</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">return</span> <span class="hljs-string">"(%s, %s)"</span>.<span class="hljs-title function_">formatted</span>(longitude, latitude); } <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">ifKnownOrElse</span>(<span class="hljs-params">Consumer<KnownLocation> action, Runnable orElse</span>) { action.<span class="hljs-title function_">accpet</span>(<span class="hljs-variable language_">this</span>); } }

record <span class="hljs-title class_">UnknownLocation</span>() <span class="hljs-keyword">impl

Options

ements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-title class_">String</span> <span class="hljs-title function_">name</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">return</span> <span class="hljs-string">"Null Island"</span>; } <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">ifKnownOrElse</span>(<span class="hljs-params">Consumer<KnownLocation> action, Runnable orElse</span>) { orElse.<span class="hljs-title function_">run</span>(); } }</pre></div><p id="4ec4">Finally, let’s put everything together and see how to use this monadic <i>Location </i>object:</p><div id="e8a6"><pre><span class="hljs-meta">@Test</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> { <span class="hljs-type">var</span> <span class="hljs-variable">people</span> <span class="hljs-operator">=</span> List.of( <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">"Alice"</span>, Location.of( <span class="hljs-number">40.7128</span>, -<span class="hljs-number">74.0060</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">"Bob"</span>, Location.of( <span class="hljs-number">51.5074</span>, -<span class="hljs-number">0.1278</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">"Charlie"</span>, Location.of(<span class="hljs-number">48.8566</span>, <span class="hljs-number">2.3522</span>)), <span class="hljs-keyword">new</span> <span class="hljs-title class_">Person</span>(<span class="hljs-string">"Tony"</span>, Location.unknown()) );

<span class="hljs-type">var</span> <span class="hljs-variable">rio</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">KnownLocation</span>(-<span class="hljs-number">22.9068</span>, -<span class="hljs-number">43.1729</span>);
people.forEach(pers -&gt; pers.location().ifKnownOrElse(
        loc -&gt; System.out.println(<span class="hljs-string">"%s is %.2f kilometers from Rio"</span>.formatted(pers.name(), loc.distanceTo(rio))),
        () -&gt; System.out.println(<span class="hljs-string">"%s is at an unknown location"</span>.formatted(pers.name()))
    )
);

}</pre></div><h2 id="9ea1">A Word On Monads</h2><p id="57b4">If a hardcore functional programmer will look over this implementation, they will quickly argue that this is not a real monad. We actually followed the pattern but only implemented what we needed for the use case at hand.</p><p id="52c7">To wrap up, if we want to follow the functional patterns in the book, we might want to implement some functions that can change the geolocation itself, without leaving the dual nature of the monadic object.</p><p id="3c76">For instance, this will be an example of a “functor” (similar to <i>Optional.map, Flux.map, or CompletableFuture.thenApply</i>):</p><div id="4848"><pre><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Location</span> { <span class="hljs-comment">// ...</span> Location <span class="hljs-title function_">moveToTheNorth</span><span class="hljs-params">(<span class="hljs-type">int</span> kms)</span>; }

<span class="hljs-keyword">record</span> <span class="hljs-title class_">KnownLocation</span><span class="hljs-params">( <span class="hljs-type">double</span> longitude, <span class="hljs-type">double</span> latitude )</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-comment">// ... </span> <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">moveToTheNorthint</span><span class="hljs-params">(<span class="hljs-type">int</span> kms)</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">KnownLocation</span>(loc.latitude + kms, loc.longitude); } }

<span class="hljs-keyword">record</span> <span class="hljs-title class_">UnknownLocation</span><span class="hljs-params">()</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Location</span> { <span class="hljs-comment">// ... </span> <span class="hljs-meta">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">moveToTheNorthint</span><span class="hljs-params">(<span class="hljs-type">int</span> kms)</span> { <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>; <span class="hljs-comment">// if it is an UnkownLocation, it will remain unkown</span> } }</pre></div><p id="9460">Additionally, a “<i>true</i>” monad will also have an equivalent of the <i>flatMap </i>method, but this will be pretty hard to showcase in our context. If you want to read more about Mondas in general, and <i>Optional</i> in particular, I’ll invite you to read one of my previous articles and let me know what you think:</p><div id="7ce1" class="link-block"> <a href="https://levelup.gitconnected.com/why-optional-is-a-monad-and-why-the-f-should-i-care-dc5ba9b3d9e1"> <div> <div> <h2>Why Optional<> is a Monad and Why the f() Should I Care?</h2> <div><h3>Please, Use Optional<> In the Intended Way!</h3></div> <div><p>levelup.gitconnected.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*UFR24o0hU9dymMQB)"></div> </div> </div> </a> </div><h2 id="43e8">Conclusion</h2><p id="ba1f">In this article, we’ve discussed the anti-patterns of branching on nullable references or exceptions. Then we leveraged polymorphism and the monad pattern to eradicate null references and unnecessary exceptions from our code.</p><p id="ec08">I invite you to review the two solutions and see how the domain model benefits from avoiding these two. In my opinion, our model is now much more expressive and the optionality of the <i>Location </i>field is now obvious.</p><p id="cf05">I encourage you to check out other articles written by <a href="undefined">Maximiliano Contieri</a> on his page: <a href="https://mcsee.medium.com/about">https://mcsee.medium.com/about</a></p><figure id="e4bb"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*vemV3DUGfrLQ2WzP"><figcaption>Photo by <a href="https://unsplash.com/@timmykp?utm_source=medium&amp;utm_medium=referral">Tim van der Kuip</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h1 id="5845">Thank You!</h1><p id="276c">Thanks for reading the article and please let me know what you think! Any feedback is welcome.</p><p id="0b26">If you want to read more about clean code, design, unit testing, object-oriented programming, functional programming, and many others, make sure to check out <a href="https://readmedium.com/start-here-6d2b065a626">my other articles</a>. Do you like the content? Consider <a href="https://medium.com/@emanueltrandafir">following or subscribing</a> to the email list.</p><p id="9b3f">Finally, if you consider becoming a Medium member and supporting my blog, here’s my <a href="https://medium.com/@emanueltrandafir/membership">referral</a>.</p><p id="8142">Happy Coding!</p><h1 id="40e1">Level Up Coding</h1><p id="4636">Thanks for being a part of our community! Before you go:</p><ul><li>👏 Clap for the story and follow the author 👉</li><li>📰 View more content in the <a href="https://levelup.gitconnected.com/?utm_source=pub&amp;utm_medium=post">Level Up Coding publication</a></li><li>💰 Free coding interview course ⇒ <a href="https://skilled.dev/?utm_source=luc&amp;utm_medium=article">View Course</a></li><li>🔔 Follow us: <a href="https://twitter.com/gitconnected">Twitter</a> | <a href="https://www.linkedin.com/company/gitconnected">LinkedIn</a> | <a href="https://newsletter.levelup.dev">Newsletter</a></li></ul><p id="cb86">🚀👉 <a href="https://jobs.levelup.dev/talent/welcome?referral=true"><b>Join the Level Up talent collective and find an amazing job</b></a></p></article></body>

Build An Expressive Domain With Monads And Polymorphism

Let’s leverage O.O.P. and F.P. patterns to address the Null-Island code smell.

Photo by Jules Bss on Unsplash

I have recently come across Maximiliano Contieri’s profile and all his insightful posts about various code smells and anti-patterns. Reading through his articles, I found one called “Null Island” and I would like to present you this anti-pattern and two alternative solutions which, in my opinion, helps you enrich your domain model.

The Null Island Code Smell

The “null island” code smell can be succinctly described as code that branches on a nullable field or relies on certain non-null values that are deemed “special by convention”. In the original example written in Kotlin, this location with coordinates (0,0) is one such special value, and it’s meant to represent a person from an unknown location:

val people = listOf(
    Person("Alice", 40.7128, -74.0060), // New York City
    Person("Bob", 51.5074, -0.1278), // London
    Person("Charlie", 48.8566, 2.3522), // Paris
    Person("Tony Hoare", 0.0, 0.0) // Null Island
)

for (person in people) {
    if (person.latitude == 0.0 && person.longitude == 0.0) {
        println("${person.name} lives on Null Island!")
    } else {
        println("${person.name} lives at (${person.latitude}, ${person.longitude}).")
    }
}

After presenting the issue, the article offers a solution that involves implementing new features such as calculating the distance between two Location objects. Notably, the final code handles any exceptions that may arise during this distance calculation by branching out and displaying a different message if the user is coming from an unknown place:

val rio = Coordinates(-22.9068, -43.1729) // Rio de Janeiro coordinates
for (person in people) {
    try {
        val distance = person.location.calculateDistance(rio)
        println("${person.name} is ${distance} kilometers from Rio de Janeiro.")
    } catch (e: IllegalArgumentException) {
        println("${person.name} is at an unknown location.")
    }
}

While this would work, I am trying to stay away from guiding the flow of the application based on exceptions. Moreover, the catch clause “swallows” the exception, leaving no trace of the issue.

In my opinion, there should be no exception here: if a user is allowed not to specify a Location, this represents a perfectly valid flow and it means the domain model should be built in such a way to reflect it.

In light of this code smell, I would like to suggest two expressive solutions. The first approach leverages the power of polymorphism by enabling the Person object to determine what information should be printed. Alternatively, the second solution adopts a monadic approach to Location implementation. By considering these two options, we can effectively tackle this issue and enrich our domain.

The “Polymorphic” Person

As you might have noticed, I’m trying to avoid null as much as possible, almost like it would not exist. I would encourage you to do the same, just don’t ever type the keyword: it will force you to understand your domain better and come up with interesting alternatives.

First solution: since the person can either specify or not a location, we should model two different representations of a Person and avoid aver holding a null reference:

interface Person {
   String name();
}

record GeolocatedPerson ( String name, Location location) implements Person {}

record UnlocatedPerson ( String name ) implements Person {}

Now, let’s “trust” the Person to tell us the distance between him and a different location on the map. We’ll polymorphically implement this in the two representations:

interface Person {
   String name();
   String distanceTo(Location location);
}

record GeolocatedPerson( String name, Location location ) implements Person {
   @Override
   public String distanceTo(Location otherLocation) {
      return "%s is %.2f kilometers from %s".formatted(
          name, location.distanceTo(otherLocation), otherLocation.name()
      );
   }
}

record UnlocatedPerson( String name ) implements Person {
   @Override
   public String distanceTo(Location otherLocation) {
      return "%s lives is at an unknown location, cannot calculate distance to %s".formatted(
          name, otherLocation.name()
      );
   }
}

Now, let’s see how we can use these different implementations of the Person interface:

@Test
void test() {
    var people = List.of(
        new GeolocatedPerson("Alice", new Location("New York", 40.7128, -74.0060)),
        new GeolocatedPerson("Bob", new Location("London", 51.5074, -0.1278)),
        new GeolocatedPerson("Charlie", new Location("Paris", 48.8566, 2.3522)),
        new UnlocatedPerson("Tony")
    );

    var rio = new Location("Rio de Janeiro", -22.9068, -43.1729);
    people.stream()
        .map(pers -> pers.distanceTo(rio))
        .forEach(dist -> System.out.println(dist));
}

The “Monadic” Location

Alternatively, we can encapsulate this logic inside the Location object. To do so, we’ll use an (almost) monadic approach. In other words, the Person will always have a Location, but the location itself can be either known or unknown.

For expressing these two possible states of the Location without keeping a null or Optional reference, we’ll use, yet again, polymorphism:

public interface Location {
    String name();
} 

record KnownLocation( double longitude, double latitude ) implements Location {
    @Override
    public String name() {
        return "(%s, %s)".formatted(longitude, latitude);
    }
}

record UnknownLocation() implements Location {
    @Override
    public String name() {
       return "Null Island";
    }
}

For convenience, we can add some static factory methods in the public interface:

public interface Location {
    String name();

    static Location of(double longitude, double latitude) {
        return new KnownLocation(longitude, latitude);
    }

    static Location unknown() {
        return new UnknownLocation();
    }
}

Lastly, we need to add the method for calculating the distance. If we add it to the common interface, the UnknownLocation will throw an exception while implementing it: this would be a clear violation of Liskov’s Substitution Principle and a bad practice overall. Therefore, we’ll only add this method inside the KnownLocation class:

record KnownLocation(double longitude, double latitude) implements Location {
    @Override
    public String name() {
        return "(%s, %s)".formatted(longitude, latitude);
    }

    public double distanceTo(KnownLocation other) { 
        retrun ...; // the calculation goes here
    }
}

Now that we have this functionality, the next question is how to make the most of it. One potential approach is to adopt a strategy similar to that used by the Optional API. For instance, we can implement methods like ifKnown or ifKnownOrElse to help us leverage the available data in a convenient and effective way:

interface Location {
    String name();

    static Location of(double longitude, double latitude) {
        return new KnownLocation(longitude, latitude);
    }

    static Location unknown() {
        return new UnknownLocation();
    } 

    void ifKnownOrElse(Consumer<KnownLocation> action, Runnable orElse);
}


record KnownLocation( double longitude, double latitude ) implements Location {
    @Override
    public String name() {
        return "(%s, %s)".formatted(longitude, latitude);
    } 
    @Override
    public void ifKnownOrElse(Consumer<KnownLocation> action, Runnable orElse) {
        action.accpet(this);
    }
}

record UnknownLocation() implements Location {
    @Override
    public String name() {
       return "Null Island";
    } 
    @Override
    public void ifKnownOrElse(Consumer<KnownLocation> action, Runnable orElse) {
        orElse.run();
    }
}

Finally, let’s put everything together and see how to use this monadic Location object:

@Test
void test() {
    var people = List.of(
        new Person("Alice", Location.of( 40.7128, -74.0060)),
        new Person("Bob", Location.of( 51.5074, -0.1278)),
        new Person("Charlie", Location.of(48.8566, 2.3522)),
        new Person("Tony", Location.unknown())
    );

    var rio = new KnownLocation(-22.9068, -43.1729);
    people.forEach(pers -> pers.location().ifKnownOrElse(
            loc -> System.out.println("%s is %.2f kilometers from Rio".formatted(pers.name(), loc.distanceTo(rio))),
            () -> System.out.println("%s is at an unknown location".formatted(pers.name()))
        )
    );
}

A Word On Monads

If a hardcore functional programmer will look over this implementation, they will quickly argue that this is not a real monad. We actually followed the pattern but only implemented what we needed for the use case at hand.

To wrap up, if we want to follow the functional patterns in the book, we might want to implement some functions that can change the geolocation itself, without leaving the dual nature of the monadic object.

For instance, this will be an example of a “functor” (similar to Optional.map, Flux.map, or CompletableFuture.thenApply):

interface Location {
    // ...
    Location moveToTheNorth(int kms);
}

record KnownLocation( double longitude, double latitude ) implements Location {
    // ... 
    @Override
    public void moveToTheNorthint(int kms) {
        return new KnownLocation(loc.latitude + kms, loc.longitude);
    }
}

record UnknownLocation() implements Location {
    // ... 
    @Override
    public void moveToTheNorthint(int kms) {
        return this;
        // if it is an UnkownLocation, it will remain unkown
    }
}

Additionally, a “true” monad will also have an equivalent of the flatMap method, but this will be pretty hard to showcase in our context. If you want to read more about Mondas in general, and Optional in particular, I’ll invite you to read one of my previous articles and let me know what you think:

Conclusion

In this article, we’ve discussed the anti-patterns of branching on nullable references or exceptions. Then we leveraged polymorphism and the monad pattern to eradicate null references and unnecessary exceptions from our code.

I invite you to review the two solutions and see how the domain model benefits from avoiding these two. In my opinion, our model is now much more expressive and the optionality of the Location field is now obvious.

I encourage you to check out other articles written by Maximiliano Contieri on his page: https://mcsee.medium.com/about

Photo by Tim van der Kuip on Unsplash

Thank You!

Thanks for reading the article and please let me know what you think! Any feedback is welcome.

If you want to read more about clean code, design, unit testing, object-oriented programming, functional programming, and many others, make sure to check out my other articles. Do you like the content? Consider following or subscribing to the email list.

Finally, if you consider becoming a Medium member and supporting my blog, here’s my referral.

Happy Coding!

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job

Functional Programming
Object Oriented
Programming
Java
Python
Recommended from ReadMedium