スマートコンストラクタ in Dart

最近読んでいた"Domain Modeling Made Functional"という本のなかで、スマートコンストラクタと呼ばれるテクニックが紹介されていてとても面白く明日から使えるなと思ったので、最近書いているDartでどのように実装できるか調べてみた。

スマートコンストラクタとは

スマートコンストラクタというのは、すべてのコンストラクタをprivateにし、代わりに有効な値ならインスタンスを返し無効な値ならエラーを返すようなコンストラクタのみを提供するような実装パターンのことを指す。

このような実装により、このクラスのインスタンスは必ず有効な値であることが保証される。そのため、あるオブジェクトをあちこちで検証する必要がなくなり、検証のための実装がスマートコンストラクタに集約されるため、凝集性の高いコードになる。また、検証漏れによる不具合も回避できるため、堅牢なコードになるとも言えるだろう。

実装例

JANコードを例にとる。JANコードは13桁または8桁の数字で構成され、"jancode"のような文字列はJANコードではない。こういったビジネスルールをスマートコンストラクタを使って実装してみる。

class Jancode {
  final String numbers;

  Jancode._internal(this.numbers);

  factory Jancode.fromNumbers(String numbers) {
    if (numbers.length != 13 || numbers.length != 8) {
      throw Exception('invalid JAN code');
    }

    // TODO: implement validation rules

    return Jancode._internal(numbers);
  }
}
void main() {
  final jancode = Jancode.fromNumbers('12345678');
}
  • Dartでは_から始まるメソッドやフィールドはprivateとして扱われる。ここではコンストラクタを_internalのように定義することでprivateにしている。
  • Dartでは必ずしも新しいインスタンスを返すわけではないコンストラクタを実装する際にはfactoryキーワードを使う。不正な値を受け取ったら例外をthrowするためにfactoryを使ったファクトリコンストラクタとして定義している。
  • 「JANコードは13桁または8桁である」といったビジネスルールをfromNumbersに実装することで、正しいJANコードのみがJancodeのインスタンスとして生成できるようになった。