🚗

[3分]プリウスとアクアから学ぶFlutterでのシングルトン

2022/03/17に公開

はじめに

今までシングルトンを使ったことがなかったので、一度詳しくまとめてみようと思います。
シングルトンの使い方についてざっくりと理解したい方は、プリウスとアクアを使ってシングルトンを体感してみるから読んでいただくのが良いかなと思います。

シングルトンとは

  • シングルトンパターンで実装されたクラスは、実行時に1つしかインスタンスが生成されない
    これだけではわかりにくいので、以下に具体例を示します。

ユースケース

  • グローバルな状態の管理する場合 => クラス間で共通のメソッド、値にアクセスできる
  • 状態(ステート)を持たないことの明示 => 複数のインスタンスを生成する必要がない
  • 使用メモリを減らしたい場合(新しいインスタンスのためにメモリを毎回確保する必要がなくなる)

以下では、ユースケースの1つ目のクラス間で共通のメソッド、値にアクセスできるという性質を例に説明していきます。

普通のクラス

class MyClass{
    //...中身
}

final instance1 = MyClass();
final instance2 = MyClass();

instance1instance2は同一のMyClassクラスからそれぞれのインスタンスを生成しています。
インスタンスが複数なので、形は同じだが内部の状態(state)は異なるオブジェクトとして扱われます。

シングルトンで実装されたクラス

class Singleton{
    //...中身
}

final instance1 = Singleton();
final instance2 = Singleton();

instance1instance2は同一のSingletonクラスで同一のインスタンスを参照しています。
同じインスタンスを参照しているので、内部の状態(state)が共通のSingletonオブジェクトです。

プログラミングを始めたばかりだと、
「同じクラスを呼んでいるんだから、内部の state も共通になってるのでは?」
と思ったりもしますが、普通は違います。
1つのインスタンス毎に 1 つのステートを持ちます。

シングルトンでクラスを実装すれば、どこからSingleton()を呼んでも同じ内部状態(ステート)になっているわけです。

シングルトンの作り方

Flutter (Dart)でのシングルトンの作り方について説明します。

  • コンストラクタSingleton._internal()をプライベート(アンダーバーをつけた状態)にし、外部から呼び出せないように
  • クラス(static な)変数にインスタンス(Singleton._internal())を格納
  • 外部から呼ばれる時には、(プライベートなインスタンスを格納した)クラス変数を返すように
class Singleton{
  static final Singleton _instance = Singleton._internal();

  factory Singleton(){
    return _instance;
  }

  Singleton._internal();
}

プリウスとアクアを使ってシングルトンを体感してみる

具体例として、プリウス(Prius)君とアクア(Aqua)君に登場してもらいましょう。
普通のクラスPriusとシングルトンクラスのAquaにそれぞれオプションをつけるメソッドを用意してやりましょう。

普通のクラス Prius

以下がプリウス君です。

class Prius {
  Prius();

  List<String> options = [];

  List<String> addOption(String option) {
    return options..add(option);
  }
}

このプリウス君にオプションをつけてやりましょう。

void main() {
  final prius1 = Prius();
  print(prius1.addOption('カーナビ')); //[カーナビ]
  print(prius1.addOption('ドラレコ')); //[カーナビ, ドラレコ]
  final prius2 = Prius();
  print(prius2.addOption('ヒーター')); //[ヒーター]
}

こちらの例では、同じプリウスでもprius1prius2は違う車になっています。
オプションとしてカーナビドラレコをつけた後に、ヒーターをつけましたが、違う車にそれぞれ取り付けてしまっています。

ここで、シングルトンで作られたアクア君にオプションをつけてやりましょう。

シングルトンクラス Aqua

以下がアクア君です。

class Aqua {
  static final Aqua _instance = Aqua._internal();
  List<String> options = [];

  factory Aqua() {
    return _instance;
  }

  Aqua._internal();

  List<String> addOption(String option) {
    return options..add(option);
  }
}

このアクア君にオプションをつけてやりましょう。

void main() {
  final aqua1 = Aqua();
  print(aqua1.addOption('カーナビ')); //[カーナビ]
  print(aqua1.addOption('ドラレコ')); //[カーナビ, ドラレコ]
  final aqua2 = Aqua();
  print(aqua2.addOption('ヒーター')); //[カーナビ, ドラレコ, ヒーター]
}

Aquaはシングルトンクラスなので、aqua1aqua2は同じ車を表しています。
したがって、aqua1aqua2にそれぞれ取り付けたオプションは、全て反映されていますね。

最後に

今回の記事で Flutter でのシングルトンの作り方・使い方は理解できましたか?
シングルトンを使ったことがない方の参考になれば幸いです。

Twitter では Flutter を中心とする技術関連の情報を発信しています!
https://twitter.com/marksaito4

お仕事の依頼は以下のメールアドレスまでご連絡をよろしくお願いします。
mark.saito@jp-gx.com

Discussion