【 Flutter 】Flutter を 基礎 から 学習 ( Dart編 ) part17 クラス

基礎 から 学ぶ Flutter 」という書籍で  学習 したことを ブログでアウトプットしていこうと思います。今回は Dart編 ( part17 )です。

前回

【 Flutter 】Flutter を 基礎 から 学習 ( Dart編 ) part16 クラス

今回もクラスについてです。

クラス

Mixin

ミックスインはクラスのコードを複数のクラス階層で再利用する方法です。
クラス階層とはなんでしょうか?「各クラス」という意味で受け取り進めてみます。

Pythonにもこの考え方があるようです。

[Python入門]多重継承とmixin:Python入門(1/2 ページ) – @IT (itmedia.co.jp)

「継承」が「基底クラスの機能を受け継ぎながら、必要に応じて、その振る舞いを変更したり、新たな機能を追加したりすることでより特殊な(具体的な)クラスへと仕立てる」ことに対して、「ミックスイン」は「既にある機能を、親子関係にない複数のクラスで共通に利用する」ものと考えられる。

こちらの説明の方が私にはわかりやすかったです😄

Mixinとして使われる側

「使われる側」はコンストラクタを宣言してはいけません。
またclassキーワードで定義してしまうと、「普通のクラス」として使用されてしまいます。
Mixinとして使用してほしい場合はclassではなくmixinキーワードを使用します。

// 通常のクラスとして使用してほしくない場合はmixinキーワードを使います。
mixin Util {
  bool isFile = false;
  bool isFolder = false;
  
  void hogehoge() {
    if(isFile) {
      print('ファイルです。');
    } else if (isFolder) {
      print('フォルダです。');
    } else {
      print('え!?');
    } 
  }
}

onキーワードを使って「Mixinを使う側」を指定することもできます。

// on を使用して「ミックスインを使う側」を制限できます。
// この場合は「Hoge系統のクラスのみ」ということになります。
mixin Util on Hoge {
  bool isFile = false;
  bool isFolder = false;
  
  void hogehoge() {
    if(isFile) {
      print('ファイルです。');
    } else if (isFolder) {
      print('フォルダです。');
    } else {
      print('え!?');
    } 
  }
}

// Hoge系統(というよりもHoge自身)なのでUtilを使用できる・・・
// と思ったのですがこれはダメ!のようです。
// なんで???
class Hoge /* with Util ダメ*/{

}

// Hogeを継承し、Hoge系統となっているのでこのクラスではUtilを使用できます。
class Fuga extends Hoge with Util{
  
}

// Hoge系統のクラスではないのでUtilを使用できません。
class Haga with Util {
  
}

いくつものMixinを使うことで同じメソッド名の定義が現れたらどうなるでしょうか?
どうやらwithで定義した「逆順」でメソッド走査を行うようです。

mixin Greet1 {
  void hello() {
    print('1');
  }
}

mixin Greet2 {
  void hello() {
    print('2');
  }
}


mixin Greet3 {
  void hello() {
    print('3');
  }
}

class Hoge with Greet1, Greet2, Greet3 {
  // 「使う側」でMixinのメソッド名と同じメソッド名を定義するとオーバーライド扱いになるようです。
  //void hello() {
  //  print('Hogeです!');
  //}
}

class Fuga extends Hoge with Greet1, Greet2 {
  
}

void main() {
  var a = Hoge();
  
  // 「Hogeです!」と出力されます。
  // Hogeクラスにhelloメソッドが無い場合は「3」と出力されます。
  a.hello();
  
  
  
  // これはどうなるでしょう?
  var b = Fuga();
  
  // 「Hogeです!」と出力されます。
  // Hogeクラスにhelloメソッドが無い場合は「2」と出力されます。
  // メソッド走査を追いかけられなくなりそうで怖いですね(笑
  // 込み入った処理かつ既存の実装が修正できないような状況では活躍しそうです。
  b.hello();
}

 

Mixinを使う側

「使う側」はwithキーワードを使い、クラスを列挙します。

// 通常のクラスとして使用してほしくない場合はmixinキーワードを使います。
mixin Util {
  bool isFile = false;
  bool isFolder = false;
  
  void hogehoge() {
    if(isFile) {
      print('ファイルです。');
    } else if (isFolder) {
      print('フォルダです。');
    } else {
      print('え!?');
    } 
  }
}

class Hoge with Util {
  void fuga(bool value) {
    // Utilとは継承関係ではないのでUtilの変数を参照できません。
    //isFIle = value;
    
    // しかしUtilのメソッドは使えます。不思議です。
    hogehoge();
  }
}

void main() {
  var a = Hoge();
  
  // あたかもHogeクラスのメソッドのように扱うことができます。
  // ここはなんとなく継承関係のクラスを扱っているような感覚を覚えます。
  // どちらかというとメソッド拡張でしょうか?
  a.hogehoge();
  
  // もちろん間接的に使用することもできます。
  a.fuga(false);
}

最後に

Mixinは継承や多数のミックスインを取り込んだ時に複雑になりそうで少し怖い印象です。
大規模開発では慎重に実装する必要がありますね。

今日はここまで!