1. What is the Mixin Design Pattern#
The mixin
design pattern can be seen as a form of multiple inheritance. So first, let's talk about why multiple inheritance syntax exists.
Cars and airplanes both belong to the category of vehicles, but airplanes can fly while cars cannot. Therefore, the behavior of flying cannot be written into the vehicle class. If each vehicle implements its own driving method, it violates the principle of code reuse (if the types of vehicles increase, it will lead to a lot of code redundancy).
Thus, to represent the behavior of flying, we need to use multiple inheritance. However, this violates the is-a
principle of inheritance relationships.
In Java, although there is no multiple inheritance, we can achieve it through interface
.
In Python, there is no interface
syntax, but it inherently supports multiple inheritance.
When using multiple inheritance, it is easy to design improperly, leading to a chaotic inheritance chain that affects mro
lookup. Therefore, our principle in programming is to avoid using multiple inheritance whenever possible by using other methods.
At this point, the Mixin
design pattern comes into play. The direct translation of Mixin
means mixing in or supplementing; it is a form of multiple inheritance. In multiple inheritance, the lookup order follows the sequence in the mro
inheritance chain.
2. Example of Mixin Design Pattern#
class Vehicle:
pass
class PlaneMixin:
def fly(self):
print("Flying")
class Airplane(Vehicle, PlaneMixin):
pass
As we can see, in the above code, the Airplane
class implements multiple inheritance. In the inheritance chain, it inherits from the Vehicle
class and the PlaneMixin
class. Here, we follow the requirements of the Mixin
design pattern by adding the suffix Mixin
to enhance the readability of the code.
The above code can be understood as Airplane
being just a Vehicle
class, not a Plane
class, and the Mixin
suffix indicates to other readers that this class is added as functionality to the subclass, not as a parent class. Its role is equivalent to Java's interface
.
This way, we do not need a complex and large inheritance chain; we can quickly construct the required subclass by choosing to combine the functionalities of different classes.
3. Principles for Using Mixin Design Pattern#
When using the Mixin
design pattern to achieve multiple inheritance, special attention should be paid to the following principles:
- First, the Mixin class must represent a specific functionality, not a specific object, similar to
Runnable
andCallable
in Java. - Second, it must represent a single responsibility; if there are multiple functionalities, we should implement multiple
Mixin
classes. - Next, the Mixin class should not depend on the implementation of subclasses and should be an abstract class that cannot be instantiated or inherit from classes other than Mixin.
- Finally, even if the subclass does not inherit from the Mixin class, it must still function normally, just with some functionalities missing.
Java interfaces provide multiple inheritance of "specifications." Mixin classes provide multiple inheritance of both "specifications" and "implementations," making them simpler to use compared to interfaces.
4. Additional Information#
In other frameworks or languages, there are similar Mixin functionalities, such as in Ruby
, Django
, Vue
, React
, and so on.