0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Dart】mixinを使いこなしたい

Last updated at Posted at 2025-03-16

機能の再利用に便利なmixin

複数のクラスで同じ処理を使い回したいときってよくありますよね?
そんなときに便利なのがmixin

一度記述した処理を簡単に複数クラスで使い回すことができます。

実装例

コードで見た方がイメージが湧くと思いますので、公式ドキュメントの実装例を見てみましょう。

定義方法

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

mixinをつけてクラスのように宣言することで他クラスから使用可能になります。

他クラスからの使用方法

// mixinを付与したいクラスはwithを使ってクラスを宣言する
class Musician extends Performer with Musical {
  // ···
}

// 複数のmixinを付与することも可能
class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

mixinを付与したいクラスはwithを使って付与したいmixinを指定します。継承と異なり、複数のmixinを付与することが可能です。

これでmixinが付与されたクラスのインスタンスから直接mixinの機能が使用可能になります!

final maestro = new Maestro();
maestro.entertainMe(); // 出力:'Waving hands'

Tips

継承、コンストラクタの実装が不可

宣言時にextendsを含めることができず、コンストラクタは実装できません。
(= デフォルトコンストラクタのみしか持てない)

これはmixinが複数付与可能であることであるためですね。
一つのクラスに複数付与する場合、引数を持つmixinが存在するとどのコンストラクタが正しいものなのかわからなくなってしまいます。

mixinが依存するメンバーの実装をサブクラスに強制させる

mixinはコンストラクタを持つことができないため、初期化のパラメータを直接呼び出し元から受け取ることができません。

つまり、mixinが依存する初期化必須なメンバの初期化はサブクラスで実装されることが必要です。

例えば、以下のような実装ではサブクラスでcountの初期化が必須ですが、このままだと初期化漏れに繋がる可能性が高いです。

mixin CounterMixin {
  late int count; // 危険: 初期化保証なし
}

class MyClass with CounterMixin {
  MyClass() {
    count = 0; // サブクラスで初期化が必要
  }
}

そんなときは以下の3つの実装方法で解決できます!

1.抽象メンバーを定義する

mixin Musician {
  void playInstrument(String instrumentName); // mixinで抽象メンバを定義する

  void playPiano() {
    playInstrument('Piano');
  }
  void playFlute() {
    playInstrument('Flute');
  }
}

class Virtuoso with Musician { 

  // サブクラスでは抽象メンバの実装が強制される
  @override
  void playInstrument(String instrumentName) { 
    print('Plays the $instrumentName beautifully');
  }  
}

2.インターフェースを実装する

// インターフェース
abstract interface class Tuner {
  void tuneInstrument();
}

// Tunerを実装するmixinを定義する
mixin Guitarist implements Tuner {
  void playSong() {
    // tuneInstrumentの実装はサブクラスに任せる
    tuneInstrument();

    print('Strums guitar majestically.');
  }
}

// Guitaristが付与されたクラスはtuneInstruementの実装が強制される
class PunkRocker with Guitarist {

  @override
  void tuneInstrument() {
    print("Don't bother, being out of tune is punk rock.");
  }
}

3.on句によるスーパークラス制約

class Musician {
  // mixinのスーパークラスに依存するメンバを定義する
  musicianMethod() {
    print('Playing music!');
  }
}

// on句を使ってMusicianに依存することを明確にする
mixin MusicalPerformer on Musician {
  performerMethod() {
    print('Performing music!');
    super.musicianMethod();
  }
}

// MusicalPerformerをmixinとして使用するクラスはMusicianのサブクラスである必要がある
class SingerDancer extends Musician with MusicalPerformer { }

main() {
  // Musicianを継承しているクラスであればperformerMethodが使用可能
  SingerDancer().performerMethod();
}

【各実装方法のまとめ】

方法 保証レベル 柔軟性 使用例
抽象メソッド コンパイル時 高い 単純な依存関係
インターフェース実装 コンパイル時 明示的な契約が必要な場合
on句 実行時 低い 既存クラス階層との契約が必要な場合

参考

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?