Android アプリによっては結構たくさんのEditTextが出て来て、それらを一つずつ検証しないといけない場面ってのはよくある。これら一つ一つに、以下のことを書いている方は多いのではないだろうか。
if (editText.getText().toString().isEmpty()) { // 入力必須エラー } if (editText.getText().toString().length < 8) { // 最小文字数エラー }
それぞれにこのような記述を書くだけで、一瞬で残念なソースコードができあがる。かつての自分もそうやって書いていて後になってこれはひどいなーと思うようになった。今回はこれを改良しよう。
ValidateManager の使用
まず使い方はこんな感じにするように作った。
ValidateManager validateManager = new ValidateManager(context); Validators validators = new Validators(); validators.add(new PresenseValidatable()); validators.add(new MinLengthValidatable(4)); validators.add(new MaxLengthValidatable(320)); validateManager.add(editText, validators); validators = new Validators(); validators.add(new MaxLengthValidatable(24)); validateManager.add(editText2, validators); validateManager.validate()
最後のvalidate()
を呼ぶと、エラーメッセージがある場合はその文字列リストが返る。これでバリデーションの使い回しが可能だ。
今回はEditText のみのバリデーションだけど、バリデーションのほとんどはEditTextだと思うので、それで良いと思う。今後他のViewにも必要になったら考え直す。
ValidateManager の実装
ここまで設計できれば後は実装するのみ。
public class ValidateManager { private Map<EditText, Validators> validateMap; private Context context; public ValidateManager(Context context) { this.context = context; validateMap = new HashMap<EditText, Validators>(); } public void add(EditText editText, Validators validators) { validateMap.put(editText, validators); } public List<String> validate() { List<String> errorMessages = new ArrayList<String>(); LogUtil.v(validateMap.size()); for (Map.Entry<EditText, Validators> keyValue : validateMap.entrySet()) { Validators validators = keyValue.getValue(); EditText editText = keyValue.getKey(); for (Validatable validatable : validators.as_list()) { ValidateDto validateResult = validatable.isValid(editText); if (validateResult.isValid()) { continue; } // validate error StringBuffer errStr = new StringBuffer(); errStr.append(editText.getContentDescription().toString()) .append(context.getString(validateResult.getMessage())); if (validatable.getTailMessage() != null) { errStr.append(validatable.getTailMessage()); } LogUtil.v(errStr.toString()); errorMessages.add(errStr.toString()); } } return errorMessages; } }
public class Validators { private List<Validatable> validateLists; public Validators() { validateLists = new ArrayList<Validatable>(); } public void add(Validatable validatable) { validateLists.add(validatable); } public List<Validatable> as_list() { return validateLists; } }
public interface Validatable { ValidateDto isValid(EditText editText); String getTailMessage(); }
public class ValidateDto { private boolean ret; private int message; public ValidateDto(boolean ret, int message) { this.ret = ret; this.message = message; } public int getMessage() { return message; } public boolean isValid() { return ret; } }
public class MinLengthValidatable implements Validatable { // default length private int minText; public MinLengthValidatable(int minText) { this.minText = minText; } @Override public ValidateDto isValid(EditText editText) { boolean isValid = true; if (editText.getText().length() < minText) { isValid = false; } ValidateDto ret = new ValidateDto(isValid, R.string.err_length_min); return ret; } @Override public String getTailMessage() { return String.valueOf(minText); } }
Validatable
を実装したクラスをどんどん作っていけば色んなバリエーションのEditTextの検証ができる!これは超便利。
こういう Interface の使い方はめっちゃ便利だよな〜。 Java使いならInterfaceは使いこなせるようになった方がいいですよ。