1. ミックスインデザインパターンとは#
ミックスイン
デザインパターンは、多重継承の一種と見なすことができます。まず、なぜこのような構文の多重継承が生じるのかについて話しましょう。
車と飛行機はどちらも交通手段に属していますが、飛行機は飛ぶことができますが、車はできません。したがって、飛行という行動は「交通手段」というクラスに書くことはできません。各交通手段がそれぞれ独自の走行方法を実装すると、コードの再利用性の原則に反します(交通手段の種類が増えるにつれて、大量のコードの重複が発生します)。
したがって、私たちは「飛行」という行動を表すために、多重継承を行う必要があります。しかし、これにより継承関係が「is-a」の原則に反します。
Java では、多重継承はありませんが、interface
を使用して多重継承を実現することができます。
Python では、interface
という構文はありませんが、多重継承をサポートしています。
多重継承を使用する際には、設計ミスが発生しやすく、継承チェーンが混乱し、mro
の検索に影響を与える可能性があるため、プログラミングの際には、「多重継承の代わりに他の方法を使用できる場合は、できるだけ多重継承を使用しない」という原則を守る必要があります。
このような場合、Mixin
デザインパターンが登場します。Mixin
は、直訳すると「混入、追加」という意味であり、多重継承の一種です。多重継承では、検索の順序はmro
の継承チェーンの順序に従って行われます。
2. ミックスインデザインパターンの例#
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 クラスはある機能を表す必要があり、ある物体を表すものではありません。これは Java の
Runnable
やCallable
と同じです。 - 次に、Mixin クラスが表す責任は単一でなければなりません。複数の機能がある場合は、複数の
Mixin
クラスを実装する必要があります。 - さらに、Mixin クラスは子クラスの実装に依存せず、抽象クラスであり、自体をインスタンス化することも、Mixin 以外のクラスを継承することもできません。
- 最後に、子クラスは Mixin クラスを継承していなくても正常に動作する必要がありますが、一部の機能が欠落して使用できない場合があります。
Java のインターフェースは、「仕様」の多重継承のみを提供しています。Mixin クラスは「仕様」と「実装」の両方の多重継承を提供し、使用上はインターフェースよりも簡単です。
4. 追加情報#
他のフレームワークや言語でも、同様の Mixin 機能があります。例えば、Ruby
、Django
、Vue
、React
などです。