nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Nullチェックを削除する方法 (NullObjectパターンとNull合体演算子)

はじめに

Nullチェックを削除する方法はいくつかあります。最適な方法を使用してください。

  • NullObjectパターン
    • オブジェクト指向プログラミングが使えれば、どの言語でも実現可能
  • Null合体演算子
    • 言語仕様にNull合体演算子があれば可能
  • その他の方法
    • Javaの場合はOptionalを使用

NullObjectパターン

NullObjectパターンを使用することで、Nullチェックのif文を削除できます。

クラス図

f:id:nprogram:20190619230456p:plain

コード例

次にように作成します。

  1. 何もない(本例では職種がない)ことを表すサブクラスであるNullEmployeeクラスを作成します。

  2. サブクラスのメソッド(本例ではgetTypeNameメソッド)は呼ばれても何も処理しないようにします

  3. CreatorクラスのfactoryMethodにおいて、不正なパラメータを受け取った場合は、NullEmployeeクラスを返します。

  4. 呼び出し元関数のおいて、Nullチェックのif文を削除します

  5. NullEmployeeクラスのインスタンスが存在するため、NullPointerExceptionの例外が発生しません。

本記事に記載されていない他のクラス(Salesman, Engineer, Manager, Employee)のコードは、以下の記事を参照ください。

switch文を爆散 - nprogram’s blog

[NullEmployee .java]

// 具象クラス
public class NullEmployee extends Employee
{
    public NullEmployee(int typeId)
    {
        _typeId = typeId;
    }
    
    @Override
    public int getTypeId()
    {
        return _typeId;
    }
    
    @Override
    public String getTypeName()
    {
        // Nullオブジェクトであるため、処理が空っぽの状態とする
        return "";
    }
}

[CreatorEmployee.java]

// 本クラスは継承を禁止するため、final修飾子を付与します
public final class CreatorEmployee
{
    // 本例では、定数の定義はCreatorEmployeeクラスが持ちます
    // もし、列挙型(Enum)を使いたいなら、以下のサイトを参考にしてください
    // https://qiita.com/KeithYokoma/items/9681b130ea132cfad64d
    
    public static final int ENGINEER = 0;
    public static final int SALESMAN = 1;
    public static final int MANAGER = 2;
    public static final int ILLEGAL_EMPLOYEE = -1;
    
    // コンストラクタをprivateにすることで、インスタンス化を禁止させましょう
    private CreatorEmployee(){ }
    
    // factoryMethodメソッドの役割は与えられたパラメータによって生成するインスタンスを切り替えること
    public static Employee factoryMethod(int type)
    {
        switch(type)
        {
            case ENGINEER:
                return new Engineer(ENGINEER);
                
            case SALESMAN:
                return new Salesman(SALESMAN);
                
            case MANAGER:
                return new Manager(MANAGER);
                
            default:
                System.out.println("Error!! Illegal Parameter.");
                return new NullEmployee(ILLEGAL_EMPLOYEE);
        }
    }
}

[Main.java]

import java.util.*;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        // (1) インスタンスを生成
        // (2) 関数に作成したインスタンスを渡す
        
        Employee engineer = createEmployee(CreatorEmployee.ENGINEER);
        showTypeName(engineer);
        showId(engineer);
        
        Employee salesman = createEmployee(CreatorEmployee.SALESMAN);
        showTypeName(salesman);
        showId(salesman);
        
        Employee manager = createEmployee(CreatorEmployee.MANAGER);
        showTypeName(manager);
        showId(manager);
        
        Employee nullEmployee = createEmployee(-9999); // 不正値を入力する
        showTypeName(nullEmployee);
        showId(nullEmployee);
    }
    
    public static Employee createEmployee(int type)
    {
        return CreatorEmployee.factoryMethod(type);
    }
    
    
    public static void showId(Employee employ)
    {
        // Nullオブジェクト適用により、null比較のif文がなくなりました。
        System.out.println(employ.getTypeName());
    }
    
    public static void showTypeName(Employee employ)
    {
        // Nullオブジェクト適用により、null比較のif文がなくなりました。
        System.out.println(employ.getTypeId());  
    }
}
0
Engineer
1
Salesman
2
Manager
Error!! Illegal Parameter.
-1

null合体演算子

言語使用に、NullObjectパターンを使用するのではなく、言語使用として、NullObjectパターンを使用しなくともNullPointerException例外を回避できる仕組みがあります。

C#では「null合体演算子」(null coalescing operator、coalesceは「合体する」という意味の動詞)を使うと、nullチェックのif文を削除できます。

以下に、コード例を記載します。

[適用前]

public class Test{
    public static void Main(){

        string documentTitle = null;
        string initialTitle = "報告書";
    
        if (documentTitle == null) {
            documentTitle = initialTitle;
        }
        System.Console.WriteLine(documentTitle);
    }
}
報告書

Null合体演算子(C#では??)を使うと以下のように記載できます。これにより、Nullチェックのif文を削除できました

[適用後]

public class Test{
    public static void Main(){

        string documentTitle = null;
        string initialTitle = "報告書";
        
        string fileName = documentTitle ?? initialTitle;
        
        System.Console.WriteLine(fileName);
    }
}
報告書

その他の方法

Java言語の場合はOptionalを使うことで、NullPointerException例外を回避できそうです。

あとがき

NullObjectパターンを使用しなくともNullPointerException例外を回避できる言語仕様があれば、積極的に使うべきだと思います。

NullObjectパターンは、あまり使われなくなりそうです。