Dartのコンストラクタについて

コンストラクタについて、なんとなく理解できたけどdartにはいくつかのバリエージョンのコンストラクタがあっていまいちできないなということで調べたことを、頭の中を筋肉に侵される前に備忘録としてまとめました。

コンストラクタに触れる前に少しだけ、クラスとインスタンスに触れておきます。

クラスはよく「設計図」、インスタンスは「実態」として例えられることが多いですね。
と言われても、私は言葉で説明されてもわからなかったので簡単に図で表します。
WorkOutクラス（設計図）があって、そこから生成されたのが筋トレをしたマッチョか、「やっぱり行くの面倒だわ」になった人がインスタンス（実態）です。


![スクリーンショット 2022-06-12 16.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530650/2659364b-97ef-251c-a28d-2ed70e69283a.png)

コードになるとこんな感じ。
``` dart
class WorkOut {
  String? menu;
  bool? isDone;
  
  // コンストラクタ
  WorkOut(this.menu,this.isDone);
}

void main () {
  final muscle = WorkOut("肩の日",true); // ※ジムに行かないという選択肢はない
  final notMuscle = WorkOut("ジムに行こうかな",false);
  print(muscle.menu); // 肩の日
  print(muscle.isDone);// true
  print(notMuscle.menu);// ジムに行こうかな
  print(notMuscle.isDone); // false
}
```

コンストラクタは、インスタンス生成時に一度だけ実行されつやつでクラスで定義されたメンバ変数（クラスで定義されているプロパティ）を初期化することができます。
![スクリーンショット 2022-06-13 8.04.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530650/d763a06d-cbe6-a536-cc69-cfab71a41e18.png)


といった感じで超ざっくりと、クラス、インスタンス、コンストラクタと触れてきましたがこれからが本題です。


### 名前付きコンストラクタ
Javaとかだと、オーバーロード（メソッドやコンストラクタを複数定義）できるみたいですがdartだとそれができないみたいです。その代替策として名前付きコンストラクタというもの使います。
``` dart
class WorkOut {
  String menu;
  bool isDone;
  
  bool isRest;
  String time;
  String drink;
  
  
  // コンストラクタ
  WorkOut(this.menu,this.isDone);
  
  // ハードモード追加
  WorkOut.superHardMode(this.isRest,this.time) : drink = "プロテイン";
}

void main () {
  final muscle = WorkOut("肩の日",true); // ※ジムに行かないという選択肢はない
  final notMuscle = WorkOut("ジムに行こうかな",false);
  
  final overWork = WorkOut.superHardMode(false,"3時間");
  
  print(muscle.menu); // 肩の日
  print(muscle.isDone);// true
  print(notMuscle.menu);// ジムに行こうかな
  print(notMuscle.isDone); // false
  
  print(overWork.isRest);// false
  print(overWork.time); // 3時間
}
```
何かいい例が思い浮かばなかったのですが、要は↑みたいにoverWorkしたい時は筋トレにsuperHardMode（任意の名称）という名前をつけてコンストラクタを定義してあげればいいだけ。簡単だね:muscle:
例は適当ですが、コンストラクタsuperHardModeが実行される前に、インスタンス変数drinkを初期化（プロテイン）します。これは**イニシャライザーリスト**呼ばれ、カンマで区切って複数の変数を初期化することができます。

### リダイレクトコンストラクタ
名前のまんまですが、同じクラスの別のコンストラクタにリダイレクトができ、あらかじめ定義してあるコンストラクタを利用してインスタンスを生成できます。
コロン (:) の後に呼び出すコンストラクタ名
``` dart 
class Musle {
  String shouting;
  Musle(): this.name("8LDK庭付き一戸建て！！");
  Musle.name(this.shouting);
}

void main() {
  var musle1 = Musle();
  print(musle1.shouting); // 8LDK庭付き一戸建て！！
  var musle2 = Musle.name("ケツのキレがバームクーヘン！");
  print(musle2.shouting); // ケツのキレがバームクーヘン！
}
```

### Factoryコンストラクタ
これが一番よくわからなかったので、Factoryコンストラクタを使用する2つのユースケースで理解することにしました
①一回作ったインスタンスはもう一度作りたくないない場合（シングルトン）
②↑で紹介した、イニシャライザーリストでfinalの値を初期化できない場合

```dart
class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache = <String, Logger>{};

  ①キャッシュに保存したインスタンスを返している
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
   
  ②json（オブジェクト）からfinalの値（name）を初期化する
  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
```
この記事は以下の情報を参考にして執筆しました。
- [A tour of the Dart language](https://dart.dev/guides/language/language-tour#named-constructors)
- [Dart のコンストラクタの基本 (できることまとめ)](https://zenn.dev/kaleidot725/articles/2021-11-13-dart-constructor#%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF)