1. Mixin デザインパターンとは#
mixin
デザインパターンは、多重継承の一種と見なすことができます。まず、なぜ多重継承という構文が存在するのかについて話しましょう。
自動車と飛行機はどちらも交通手段ですが、飛行機は飛ぶことができ、自動車はそれができません。したがって、飛行という行動は交通手段というクラスに書くことができません。もし各交通手段がそれぞれ独自の走行方法を実装した場合、コードの再利用の原則に反することになります(交通手段の種類が増えると、大量のコード冗長が発生します)。
したがって、飛行という行動を表現するためには、多重継承が必要です。しかし、そうすると、継承関係はis-a
の原則に反することになります。
Java では多重継承はありませんが、interface
を使用することで多重継承を実現できます。
Python にはinterface
という構文はありませんが、もともと多重継承をサポートしています。
多重継承を使用する際には、設計が不適切になりやすく、継承チェーンが混乱し、mro
の検索に影響を与える可能性があります。したがって、プログラミングの際の原則は、他の方法で多重継承を代替できる場合は、できるだけ多重継承を使用しないことです。
このような時にMixin
デザインパターンが登場します。Mixin
は直訳すると混入、補充という意味であり、多重継承の一種です。多重継承において、検索順序はmro
継承チェーンの順序に従って行われます。
2. Mixin デザインパターンの例#
class Vehicle:
pass
class PlaneMixin:
def fly(self):
print("飛行中")
class Airplane(Vehicle, PlaneMixin):
pass
上記のコードでは、Airplane
クラスが多重継承を実現しています。継承チェーン上で、Vehicle
クラスとPlaneMixin
クラスを継承しています。ここでは、Mixin
デザインパターンの要件に従い、後ろにMixin
という接尾辞を追加してコードの可読性を高めています。
上記のコードはこう理解できます。Airplane
はVehicle
クラスの一種であり、Plane
クラスではありません。そして、Mixin
接尾辞は、他の読者に対して、このクラスは機能を子クラスに追加するためのものであり、親クラスとしてではないことを示しています。その役割は Java のinterface
に相当します。
こうすることで、複雑で巨大な継承チェーンを必要とせず、異なるクラスの機能を組み合わせるだけで、必要な子クラスを迅速に構築できます。
3. Mixin デザインパターンを使用する際の原則#
Mixin
デザインパターンを使用して多重継承を実現する際には、以下の原則に特に注意する必要があります:
- まず、Mixin クラスは特定の機能を表すものであり、特定の物体を表すものではありません。この点は Java の
Runnable
やCallable
と同じです。 - 次に、表す責任は単一でなければなりません。複数の機能がある場合は、複数の
Mixin
クラスを実装するべきです。 - 次に、Mixin クラスは子クラスの実装に依存せず、抽象クラスであり、自身はインスタンス化できず、Mixin 以外のクラスを継承することもできません。
- 最後に、子クラスが Mixin クラスを継承していなくても、通常通り動作しなければなりません。ただし、一部の機能が欠けて使用できない場合があります。
Java のインターフェースは、「仕様」の多重継承のみを提供します。Mixin クラスは「仕様」と「実装」の多重継承を同時に提供し、インターフェースに比べて使用が簡単です。
4. 補足#
他のフレームワークや言語にも、Ruby
、Django
、Vue
、React
など、類似の Mixin 機能があります。