Refactoring 015 – Remove NULL

Refactorings (21 Part Series)

1 Refactoring 001 – Remove Setters
2 Refactoring 002 – Extract Method
17 more parts…
3 Refactoring 003 – Extract Constant
4 Refactoring 004 – Remove Unhandled Exceptions
5 Refactoring 005 – Replace Comment with Function Name
6 Refactoring 006 – Rename Result Variables
7 Refactoring 007 – Extract Class
8 Refactoring 008 – Convert Variables to Constant
9 Refactoring 009 – Protect Public Attributes
10 Refactoring 010 – Extract Method Object
11 Refactoring 011 – Replace Comments with Tests
12 Refactoring 012 – Reify Associative Arrays
13 Refactoring 013 – Remove Repeated Code
14 Refactoring 014 – Remove IF
15 Refactoring 015 – Remove NULL
16 Refactoring 016 – Build With The Essence
17 Refactoring 017 – Convert Attributes to Sets
18 Refactoring 018 – Replace Singleton
19 Refactoring 019 – Reify Email Addresses
20 Refactoring 020 – Transform Static Functions
21 Refactoring 021 – Remove Dead Code

Eliminating The Billion-Dollar Mistake forever

TL;DR: Use the Null Object Pattern to eliminate null checks and simplify your code.

Problems Addressed

Related Code Smells

图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 12 – Null

Maxi Contieri ・ Oct 31 ’20

#codenewbie #tutorial #oop
图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 260 – Crowdstrike NULL

Maxi Contieri ・ Jul 20

#security #cleancode #hacking #null
图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 149 – Optional Chaining

Maxi Contieri ・ Jul 16 ’22

#javascript #webdev #beginners #programming
图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 212 – Elvis Operator

Maxi Contieri ・ May 13 ’23

#webdev #javascript #beginners #programming
图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 192 – Optional Attributes

Maxi Contieri ・ Jan 17 ’23

#webdev #javascript #beginners #programming
图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 126 – Fake Null Object

Maxi Contieri ・ Apr 2 ’22


图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Code Smell 208 – Null Island

Maxi Contieri ・ Apr 19 ’23

#webdev #beginners #programming #kotlin

Steps

These steps are a special case of Remove IF Refactoring

  1. Create a Null Object class that implements the same interface

  2. Replace null checks with the polymorphic Null Object

Sample Code

Before

<span>public</span> <span>class</span> <span>SoccerMatch</span> <span>{</span>
<span>private</span> <span>Team</span> <span>homeTeam</span><span>;</span>
<span>private</span> <span>Team</span> <span>awayTeam</span><span>;</span>
<span>private</span> <span>TimeRecord</span> <span>regularTime</span><span>;</span>
<span>private</span> <span>TimeRecord</span> <span>extraTime</span><span>;</span>
<span>public</span> <span>SoccerMatch</span><span>(</span><span>Team</span> <span>homeTeam</span><span>,</span>
<span>Team</span> <span>awayTeam</span><span>,</span>
<span>TimeRecord</span> <span>regularTime</span><span>,</span>
<span>TimeRecord</span> <span>extraTime</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>homeTeam</span> <span>=</span> <span>homeTeam</span><span>;</span>
<span>this</span><span>.</span><span>awayTeam</span> <span>=</span> <span>awayTeam</span><span>;</span>
<span>this</span><span>.</span><span>regularTime</span> <span>=</span> <span>regularTime</span><span>;</span>
<span>this</span><span>.</span><span>extraTime</span> <span>=</span> <span>extraTime</span><span>;</span>
<span>}</span>
<span>public</span> <span>int</span> <span>totalGoals</span><span>()</span> <span>{</span>
<span>int</span> <span>goals</span> <span>=</span> <span>regularTime</span><span>.</span><span>goals</span><span>();</span>
<span>// You might forget this if check </span>
<span>// resulting in a null error</span>
<span>if</span> <span>(</span><span>extraTime</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span>
<span>goals</span> <span>+=</span> <span>extraTime</span><span>.</span><span>goals</span><span>();</span>
<span>}</span>
<span>return</span> <span>goals</span><span>;</span>
<span>}</span>
<span>}</span>
<span>class</span> <span>TimeRecord</span> <span>{</span>
<span>private</span> <span>int</span> <span>goals</span><span>;</span>
<span>public</span> <span>TimeRecord</span><span>(</span><span>int</span> <span>goals</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>goals</span> <span>=</span> <span>goals</span><span>;</span>
<span>}</span>
<span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
<span>return</span> <span>goals</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>SoccerMatch</span> <span>{</span>
    <span>private</span> <span>Team</span> <span>homeTeam</span><span>;</span>
    <span>private</span> <span>Team</span> <span>awayTeam</span><span>;</span>
    <span>private</span> <span>TimeRecord</span> <span>regularTime</span><span>;</span>
    <span>private</span> <span>TimeRecord</span> <span>extraTime</span><span>;</span>

    <span>public</span> <span>SoccerMatch</span><span>(</span><span>Team</span> <span>homeTeam</span><span>,</span> 
                       <span>Team</span> <span>awayTeam</span><span>,</span>
                       <span>TimeRecord</span> <span>regularTime</span><span>,</span> 
                       <span>TimeRecord</span> <span>extraTime</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>homeTeam</span> <span>=</span> <span>homeTeam</span><span>;</span>
        <span>this</span><span>.</span><span>awayTeam</span> <span>=</span> <span>awayTeam</span><span>;</span>
        <span>this</span><span>.</span><span>regularTime</span> <span>=</span> <span>regularTime</span><span>;</span>
        <span>this</span><span>.</span><span>extraTime</span> <span>=</span> <span>extraTime</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>int</span> <span>totalGoals</span><span>()</span> <span>{</span>
        <span>int</span> <span>goals</span> <span>=</span> <span>regularTime</span><span>.</span><span>goals</span><span>();</span>
        <span>// You might forget this if check </span>
        <span>// resulting in a null error</span>
        <span>if</span> <span>(</span><span>extraTime</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span>
            <span>goals</span> <span>+=</span> <span>extraTime</span><span>.</span><span>goals</span><span>();</span>
        <span>}</span>
        <span>return</span> <span>goals</span><span>;</span>
    <span>}</span>
<span>}</span>

<span>class</span> <span>TimeRecord</span> <span>{</span>
    <span>private</span> <span>int</span> <span>goals</span><span>;</span>

    <span>public</span> <span>TimeRecord</span><span>(</span><span>int</span> <span>goals</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>goals</span> <span>=</span> <span>goals</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
        <span>return</span> <span>goals</span><span>;</span>
    <span>}</span>
<span>}</span>
public class SoccerMatch { private Team homeTeam; private Team awayTeam; private TimeRecord regularTime; private TimeRecord extraTime; public SoccerMatch(Team homeTeam, Team awayTeam, TimeRecord regularTime, TimeRecord extraTime) { this.homeTeam = homeTeam; this.awayTeam = awayTeam; this.regularTime = regularTime; this.extraTime = extraTime; } public int totalGoals() { int goals = regularTime.goals(); // You might forget this if check // resulting in a null error if (extraTime != null) { goals += extraTime.goals(); } return goals; } } class TimeRecord { private int goals; public TimeRecord(int goals) { this.goals = goals; } public int goals() { return goals; } }

Enter fullscreen mode Exit fullscreen mode

After

<span>// 1. Create a Null Object class that implements the same interface</span>
<span>interface</span> <span>TimeRecord</span> <span>{</span>
<span>// The common protocol between the real object </span>
<span>// and the Null Object</span>
<span>int</span> <span>goals</span><span>();</span>
<span>}</span>
<span>class</span> <span>PlayedTimeRecord</span> <span>implements</span> <span>TimeRecord</span> <span>{</span>
<span>// This class is suitable both to be</span>
<span>// a Regular Time or an Extra Time</span>
<span>private</span> <span>int</span> <span>goals</span><span>;</span>
<span>public</span> <span>PlayedTimeRecord</span> <span>(</span><span>int</span> <span>goals</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>goals</span> <span>=</span> <span>goals</span><span>;</span>
<span>}</span>
<span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
<span>return</span> <span>goals</span><span>;</span>
<span>}</span>
<span>}</span>
<span>class</span> <span>NoExtraTime</span> <span>implements</span> <span>TimeRecord</span> <span>{</span>
<span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
<span>// They are polymorphic</span>
<span>// They don't need IF checks</span>
<span>return</span> <span>0</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>SoccerMatch</span> <span>{</span>
<span>private</span> <span>Team</span> <span>homeTeam</span><span>;</span>
<span>private</span> <span>Team</span> <span>awayTeam</span><span>;</span>
<span>private</span> <span>PlayedTimeRecord</span> <span>regularTime</span><span>;</span>
<span>private</span> <span>TimeRecord</span> <span>extraTime</span><span>;</span>
<span>public</span> <span>SoccerMatch</span><span>(</span><span>Team</span> <span>homeTeam</span><span>,</span>
<span>Team</span> <span>awayTeam</span><span>,</span>
<span>PlayedTimeRecord</span> <span>regularTime</span><span>,</span>
<span>TimeRecord</span> <span>extraTime</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>homeTeam</span> <span>=</span> <span>homeTeam</span><span>;</span>
<span>this</span><span>.</span><span>awayTeam</span> <span>=</span> <span>awayTeam</span><span>;</span>
<span>// There's a business rule telling</span>
<span>// regular time is not optional</span>
<span>// Therefore is an instance of PlayedTimeRecord</span>
<span>this</span><span>.</span><span>regularTime</span> <span>=</span> <span>regularTime</span><span>;</span>
<span>this</span><span>.</span><span>extraTime</span> <span>=</span> <span>extraTime</span><span>;</span>
<span>}</span>
<span>public</span> <span>int</span> <span>totalGoals</span><span>()</span> <span>{</span>
<span>// 2. Replace null checks with the polymorphic Null Object</span>
<span>// No Ifs </span>
<span>// No null checks</span>
<span>return</span> <span>regularTime</span><span>.</span><span>goals</span><span>()</span> <span>+</span> <span>extraTime</span><span>.</span><span>goals</span><span>();</span>
<span>}</span>
<span>}</span>
<span>// 1. Create a Null Object class that implements the same interface</span>

<span>interface</span> <span>TimeRecord</span> <span>{</span>
    <span>// The common protocol between the real object </span>
    <span>// and the Null Object</span>
    <span>int</span> <span>goals</span><span>();</span>
<span>}</span>

<span>class</span> <span>PlayedTimeRecord</span> <span>implements</span> <span>TimeRecord</span> <span>{</span>
    <span>// This class is suitable both to be</span>
    <span>// a Regular Time or an Extra Time</span>
    <span>private</span> <span>int</span> <span>goals</span><span>;</span>

    <span>public</span> <span>PlayedTimeRecord</span> <span>(</span><span>int</span> <span>goals</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>goals</span> <span>=</span> <span>goals</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
        <span>return</span> <span>goals</span><span>;</span>
    <span>}</span>
<span>}</span>

<span>class</span> <span>NoExtraTime</span> <span>implements</span> <span>TimeRecord</span> <span>{</span>
    <span>public</span> <span>int</span> <span>goals</span><span>()</span> <span>{</span>
        <span>// They are polymorphic</span>
        <span>// They don't need IF checks</span>
        <span>return</span> <span>0</span><span>;</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>SoccerMatch</span> <span>{</span>
    <span>private</span> <span>Team</span> <span>homeTeam</span><span>;</span>
    <span>private</span> <span>Team</span> <span>awayTeam</span><span>;</span>
    <span>private</span> <span>PlayedTimeRecord</span> <span>regularTime</span><span>;</span>
    <span>private</span> <span>TimeRecord</span> <span>extraTime</span><span>;</span>

    <span>public</span> <span>SoccerMatch</span><span>(</span><span>Team</span> <span>homeTeam</span><span>,</span> 
                       <span>Team</span> <span>awayTeam</span><span>,</span>
                       <span>PlayedTimeRecord</span> <span>regularTime</span><span>,</span>
                       <span>TimeRecord</span> <span>extraTime</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>homeTeam</span> <span>=</span> <span>homeTeam</span><span>;</span>
        <span>this</span><span>.</span><span>awayTeam</span> <span>=</span> <span>awayTeam</span><span>;</span>        
        <span>// There's a business rule telling</span>
        <span>// regular time is not optional</span>
        <span>// Therefore is an instance of PlayedTimeRecord</span>
        <span>this</span><span>.</span><span>regularTime</span> <span>=</span> <span>regularTime</span><span>;</span>
        <span>this</span><span>.</span><span>extraTime</span> <span>=</span> <span>extraTime</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>int</span> <span>totalGoals</span><span>()</span> <span>{</span>
        <span>// 2. Replace null checks with the polymorphic Null Object</span>
        <span>// No Ifs </span>
        <span>// No null checks</span>
        <span>return</span> <span>regularTime</span><span>.</span><span>goals</span><span>()</span> <span>+</span> <span>extraTime</span><span>.</span><span>goals</span><span>();</span>
    <span>}</span>
<span>}</span>
// 1. Create a Null Object class that implements the same interface interface TimeRecord { // The common protocol between the real object // and the Null Object int goals(); } class PlayedTimeRecord implements TimeRecord { // This class is suitable both to be // a Regular Time or an Extra Time private int goals; public PlayedTimeRecord (int goals) { this.goals = goals; } public int goals() { return goals; } } class NoExtraTime implements TimeRecord { public int goals() { // They are polymorphic // They don't need IF checks return 0; } } public class SoccerMatch { private Team homeTeam; private Team awayTeam; private PlayedTimeRecord regularTime; private TimeRecord extraTime; public SoccerMatch(Team homeTeam, Team awayTeam, PlayedTimeRecord regularTime, TimeRecord extraTime) { this.homeTeam = homeTeam; this.awayTeam = awayTeam; // There's a business rule telling // regular time is not optional // Therefore is an instance of PlayedTimeRecord this.regularTime = regularTime; this.extraTime = extraTime; } public int totalGoals() { // 2. Replace null checks with the polymorphic Null Object // No Ifs // No null checks return regularTime.goals() + extraTime.goals(); } }

Enter fullscreen mode Exit fullscreen mode

As a side note when you prompt Gemini-AI with the After version it says:

While the code is well-structured, there’s a minor optimization opportunity.
The NoExtraTime class can be eliminated without compromising the code’s
functionality.

Type

[X] Semi-Automatic

The refactoring has a semantic part that needs to find an existing NullObejct in the domain.

Safety

This refactoring is generally safe.

You must ensure that the Null Object’s behavior matches the expected behavior for real cases in your code.

The null object needs to be partially polymorphic with real ones.

In strongly typed languages, they must adhere to the same interface or belong to the same hierarchy.

Why is the code better?

You eliminate null checks with accidental IF conditions, making the code cleaner and easier to understand.

The Null Object Pattern ensures you always work with objects, avoiding null pointer exceptions – The Billion Dollar Mistake.

Limitations

Tags

  • Null

Related Refactorings

图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Refactoring 014 – Remove IF

Maxi Contieri ・ Jul 5

#webdev #beginners #programming #java

See also

图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

Null: The Billion dollar mistake

Maxi Contieri ・ Nov 18 ’20

#codenewbie #tutorial #programming #webdev

Wikipedia

Refactoring Guru


This article is part of the Refactoring Series.

图片[1]-Refactoring 015 - Remove NULL - 拾光赋-拾光赋

How to Improve your Code With easy Refactorings

Maxi Contieri ・ Oct 24 ’22

#webdev #beginners #programming #tutorial

Refactorings (21 Part Series)

1 Refactoring 001 – Remove Setters
2 Refactoring 002 – Extract Method
17 more parts…
3 Refactoring 003 – Extract Constant
4 Refactoring 004 – Remove Unhandled Exceptions
5 Refactoring 005 – Replace Comment with Function Name
6 Refactoring 006 – Rename Result Variables
7 Refactoring 007 – Extract Class
8 Refactoring 008 – Convert Variables to Constant
9 Refactoring 009 – Protect Public Attributes
10 Refactoring 010 – Extract Method Object
11 Refactoring 011 – Replace Comments with Tests
12 Refactoring 012 – Reify Associative Arrays
13 Refactoring 013 – Remove Repeated Code
14 Refactoring 014 – Remove IF
15 Refactoring 015 – Remove NULL
16 Refactoring 016 – Build With The Essence
17 Refactoring 017 – Convert Attributes to Sets
18 Refactoring 018 – Replace Singleton
19 Refactoring 019 – Reify Email Addresses
20 Refactoring 020 – Transform Static Functions
21 Refactoring 021 – Remove Dead Code

原文链接:Refactoring 015 – Remove NULL

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
Sometimes you have to be your own hero.
有时候必须做自己的英雄
评论 抢沙发

请登录后发表评论

    暂无评论内容