4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter】Freezedでオブジェクト初期化時に値をバリデートする方法

Last updated at Posted at 2022-07-26

はじめに

皆さん、Freezedを使ってますでしょうか?
簡単な記述で、Jsonのシリアライズ・デシリアライズや、オブジェクト同士の比較のオーバライドメソッドなどを生成してくれるFreezedは大変便利です。
特に便利に感じるというか、必須だと思うのが単体テストを書こうと思ったときです。
Stringとかintのプリミティブ型を多用せず、オブジェクト指向に基づき、丁寧にクラス化を行っていると、
比較するオブジェクトに「==」と「hashCode」が必要になってくるので、
それをいちいち実装するのは結構手間になってきます。

また不変オブジェクトとして生成できるので、値オブジェクトにも使えるというのも利点かと思います。
本記事は値オブジェクトにFreezedを活用する際、コンストラクタで引数の値のバリデートを行う場合、どうすればいいかというプラクティスになります。
経緯として、値オブジェクトを表現する上で、初期化時に不正な値のチェックを行いたい(行わないと生焼けオブジェクトになる)という場面がありましたが、実装方法がわからなかったので調査したので、他にも困っている人がいるかと思い、アウトプットすることにしました。

対象読者

・Freezedを使ってコンストラクタで初期値のバリデーションを行いたい方

目次

  1. 概要
  2. サンプルコード
  3. 参考文献

概要

方法としてはシンプルで、コンストラクタにバリデーションを追加します。
一応、これだけでオブジェクト初期化時にバリデーションを行えて、不正な値だった場合、例外を投げるといった処理が可能となります。
しかし、上記の方法には一点だけ問題があります。
それは、自動生成されるcopyWithメソッド内でバリデーションを実装したコンストラクタが呼ばれないことです。
つまり、copyWithを使うと不正な値が混入する可能性があるということです。
この問題に対処するべく、copyWithは自動ではなく、自分で実装するという方法をとりました。
(もっといい方法あれば教えてください...。)

サンプルコード

ユーザーの声明を表現するUserNameという値オブジェクトをFreezedで作成するというケースでサンプルコードを載せておきます。

import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_name.freezed.dart';

// 大文字で@Freezedとすると、後ろに()書きで不要なものを削除できる
// 初期化する場合はcopywithを生成しないように指定する
// Freezedで自動生成された「copyWith」メソッドでは
// バリデーションを実装したコンストラクタ(サンプルコードでいうと「factory UserName」の部分)が呼び出されないため、
// copyWithは自分で実装する
@Freezed(copyWith: false)
abstract class UserName implements _$UserName{
  UserName._();

  factory UserName._internal({
    required String firstName,
    required String lastName,
  }) = _UserName;

  factory UserName({
    required String firstName,
    required String lastName,
  }){
    if(firstName.isEmpty){
      throw Exception("firstName is Empty.");
    }
    if(lastName.isEmpty){
      throw Exception("lastName is Empty.");
    }

    return UserName._internal(
      firstName : firstName,
      lastName : lastName,
    );
  }

  // バリデーションを実装したコンストラクタを呼び出すようにcopyWithを実装
  UserName copyWith({String? firstName, String? lastName}){
    final copiedFirstName = firstName ?? this.firstName;
    final copiedLastName = lastName ?? this.lastName;

    return UserName(firstName: copiedFirstName, lastName: copiedLastName);
  }
}

参考文献

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?