はじめに

インタフェース開発等で必須区分等のバリデーションを大量に行う必要があって、さらにインタフェース機能によって同一項目でも必須と任意が変わることがある。

たとえば、作成機能では必須(not null, not empty)の項目が更新機能では空文字以外の任意(nullable, not empty)になったりする。あるいは何かのフラグが立っているときは必須で、立っていなければ任意など。

そのような場合、各項目でバリデーションメソッドを呼ぶ度にisNullableかどうか等を考えるよりも、設計書の必須/任意等をそのままコードにした方がシンプルになる。

機能と必須区分で表を作って、設計書を見ながら全項目について「必須/任意/条件付き必須/空文字以外の任意」を判断してバリデーションのコーディングしてもいいが、各項目について毎回判断するのではなく設計書の情報をそのままコーディングし、必須区分がバリデーションメソッドの中で自動で求められるようにする。

感覚としては、各項目に設計情報をattributeとして付与し、各項目自体が、自身がnullを許容するのかemptyを許容するのかを判断できるようにする。

設計書

作成

項目 必須 備考
item1 True <3

更新

項目 必須 備考
item1 False <3 空文字は不可

コード

前提

// 項目値
String value = "test";
// 作成か更新か
IfType ifType = IfType.CREATE;

毎回考えるパターン

boolean permitNull = (ifType != IfType.CREATE);
boolean permitEmpty = false;
validateRequired(permitNull, permitEmpty, value, "item1");
validateFormat(value, 3, "item1");

設計書の情報をそのままコーディングするパターン

IfItemDefinition.create()
        .define(IfType.CREATE, Required.REQUIRED)
        .define(IfType.UPDATE, Required.NOT_EMPTY)
        .setMaxLen(3)
        .validate(value, ifType, "item1");
//      →item1は3文字以内で入力してください。

このパターンだと設計書の情報をそのまま書けばいいが、さきほどのパターンはvalidateRequiredの引数でnullを許容していいか空文字を許容していいかを全項目分考えなくてはいけない。

設計書の情報をそのままコーディングするパターンに登場するIfItemDefinition

import java.util.HashMap;
import java.util.Map;

public class IfItemDefinition {

    public enum Required {
        /** 必須 */
        REQUIRED,

        /** 任意 */
        OPTIONAL,

        /** 任意※EmptyはNG, NULLはOK */
        NOT_EMPTY,
    }

    public enum IfType {
        /** 新規 */
        CREATE,

        /** 更新 */
        UPDATE,
    }

    /**
     * コンストラクタ
     */
    private IfItemDefinition() {
    }

    /**
     * 必須区分定義保存
     */
    private Map<IfType, Required> map = new HashMap<>();

    /**
     * インスタンス作成
     * @return IfItemDefinition
     */
    public static IfItemDefinition create() {
        return new IfItemDefinition();
    }

    /**
     * 定義
     * @param ifType IFタイプ
     * @param required 必須区分
     * @return IfItemDefinition
     */
    public IfItemDefinition define(IfType ifType, Required required) {
        map.put(ifType, required);
        return this;
    }

    /**
     * @param ifType IFタイプ
     * @return [0]:Nullを許容するかどうか、[1]:Emptyを許容するかどうか
     */
    public boolean[] permitNullEmpty(IfType ifType) {
        boolean permitNull = false;
        boolean permitEmpty = false;

        Required required = map.get(ifType);

        switch (required) {
        case REQUIRED :
            permitNull = false;
            permitEmpty = false;
            break;

        case OPTIONAL :
            permitNull = true;
            permitEmpty = true;
            break;

        case NOT_EMPTY :
            permitNull = true;
            permitEmpty = false;
            break;
        }

        return new boolean[]{permitNull, permitEmpty};
    }

    /** 初期値 */
    private static final int INI_NUM = -1;
    /** 最大桁数 */
    private int maxLen = INI_NUM;
    /** 最大桁数をセットする */
    public IfItemDefinition setMaxLen(int maxLen) {
        this.maxLen = maxLen;
        return this;
    }

    /**
     * バリデーションの実行
     * @param value 値
     * @param ifType IFタイプ
     * @param itemName 項目名
     * @return IfItemDefinition
     */
    public IfItemDefinition validate(String value, IfType ifType, String itemName) {
        boolean[] permitNullEmpty = permitNullEmpty(ifType);
        if (!permitNullEmpty[0] && value == null) {
            System.out.println(itemName + "にnullは設定できません。");
            return this;
        }
        if (!permitNullEmpty[1] && value.equals("")) {
            System.out.println(itemName + "に空文字は設定できません。");
            return this;
        }
        if (maxLen != INI_NUM && value.length() > maxLen) {
            System.out.println(itemName + "は" + maxLen + "文字以内で入力してください。");
            return this;
        }

        System.out.println("ok");
        return this;
    }
}