Menu
Coddy logo textTech

Mixin vs 継承

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 54/110。

ミックスインの仕組みを学んだところで、継承の代わりにいつそれらを使用すべきかを明確にしましょう。主な違いは、それらが表現する関係性にあります。

継承は「is-a」関係を表します。DogAnimalの一種(is an)です。サブクラスは親クラスの特化したバージョンであり、1つのクラスのみを継承(extend)できます。

一方、ミックスインは「できる(can-do)」関係を表します。Dogは泳ぐことができますが、泳ぐことが犬を定義するわけではありません。

// 継承: Dog は Animal である (IS-A関係)
class Animal {
  String name;
  Animal(this.name);
}

class Dog extends Animal {
  Dog(String name) : super(name);
}

// Mixin: Dog は泳ぐことができる (ただし、泳ぐことによって定義されるわけではない)
mixin Swimming {
  void swim() => print('Swimming');
}

class Dog extends Animal with Swimming {
  Dog(String name) : super(name);
}

クラスが根本的なアイデンティティを共有しており、親クラスにコンストラクタが必要な場合には、継承を使用します。共通の祖先を共有しないクラスに機能を追加したい場合には、ミックスインを使用します。

特徴継承ミックスイン
関係is-acan-do
制限親は1つのみ複数のミックスイン
コンストラクタ可能不可
ユースケース特殊化振る舞いの共有

何より素晴らしいのは、これら両方を組み合わせることができる点です。1つのクラスを継承しつつ、複数の機能をミックスインすることで、クラスにまさに必要としている機能を持たせることができます。

challenge icon

チャレンジ

簡単

継承とミックスインをいつ使用すべきかを示すワーカーシステムを構築しましょう。「is-a(〜である)」関係のためのベースクラスと、「can-do(〜ができる)」能力のためのミックスインを作成し、それらを組み合わせて両方のアプローチがどのように連携するかを確認します。

コードを3つのファイルに整理します:

  • worker.dart: すべてのワーカーの核となるアイデンティティを確立するベースクラスを定義します:
    • String nameString department プロパティを持つ Worker クラス。両方の値を受け取るコンストラクタを含めます。[name] works in [department] と出力する introduce() メソッドを追加します。
  • skills.dart: ワーカーが持つことができる能力(「can-do」関係)を表すミックスインを定義します:
    • Writing code... と出力する writeCode() メソッドを持つ CanCode ミックスイン。
    • Creating design... と出力する createDesign() メソッドを持つ CanDesign ミックスイン。
    • Leading the team... と出力する leadTeam() メソッドを持つ CanManage ミックスイン。
  • main.dart: 両方のファイルをインポートし、継承とミックスインを組み合わせた特化型のワーカータイプを作成します:
    • Worker を継承し、CanCode ミックスインを使用する Developer クラス。
    • Worker を継承し、CanCodeCanManage の両方のミックスインを使用する TechLead クラス。
    • 'Engineering' 部署に 'Alice' という名前の Developer を作成します。
    • 'Engineering' 部署に 'Bob' という名前の TechLead を作成します。
    • Aliceの場合:introduce() を呼び出し、次に writeCode() を呼び出します。
    • 空行を出力します。
    • Bobの場合:introduce()writeCode()、そして leadTeam() を順に呼び出します。

DeveloperTechLead の両方がワーカー(継承)でありながら、それぞれ異なること(ミックスイン)ができる点に注目してください。TechLead は多重継承を必要とせずに、複数の能力を組み合わせています。

期待される出力:

Alice works in Engineering
Writing code...

Bob works in Engineering
Writing code...
Leading the team...

チートシート

継承は「is-a」関係(サブクラスは親の特殊化されたバージョン)を表し、ミックスインは「can-do」関係(アイデンティティを定義することなく機能を追加する)を表します。

// 継承: DogはAnimalである (IS-A)
class Animal {
  String name;
  Animal(this.name);
}

class Dog extends Animal {
  Dog(String name) : super(name);
}

// ミックスイン: Dogは泳ぐことができる (CAN-DO)
mixin Swimming {
  void swim() => print('Swimming');
}

class Dog extends Animal with Swimming {
  Dog(String name) : super(name);
}
特徴継承ミックスイン
関係is-acan-do
制限親は1つのみ複数のミックスインが可能
コンストラクタ許可不可
ユースケース特殊化共有される振る舞い

1つのクラスを継承し、複数の機能をミックスインすることで、両方のアプローチを組み合わせることができます。

class TechLead extends Worker with CanCode, CanManage {
  TechLead(String name, String department) : super(name, department);
}

自分で試してみよう

import 'worker.dart';
import 'skills.dart';

// TODO: Worker を継承し、CanCode ミックスインを使用する Developer クラスを作成します

// TODO: Worker を継承し、CanCode と CanManage の両方のミックスインを使用する TechLead クラスを作成します

void main() {
  // TODO: 'Engineering' 部門に 'Alice' という名前の Developer を作成します
  
  // TODO: Alice に対して introduce() を呼び出し、次に writeCode() を呼び出します
  
  // TODO: 空行を出力します
  
  // TODO: 'Engineering' 部門に 'Bob' という名前の TechLead を作成します
  
  // TODO: Bob に対して introduce()、writeCode()、そして leadTeam() を呼び出します
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン