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 this kind of syntax of multiple inheritance exists.
Cars and airplanes both belong to the category of transportation, but airplanes can fly, while cars cannot. Therefore, the behavior of flying cannot be written in the class of transportation. If each transportation implements its own method of travel, it violates the principle of reusing code as much as possible (if there are more and more types of transportation, it will cause a lot of code redundancy).
Therefore, in order to represent the behavior of flying, we need to use multiple inheritance. But in doing so, we violate the principle that inheritance relationships must be based on the "is-a" principle.
In Java, although there is no multiple inheritance, we can achieve multiple inheritance through interface
.
In Python, there is no syntax for interface
, but it supports multiple inheritance itself.
When using multiple inheritance, it is easy to design improperly, resulting in a messy inheritance chain and affecting the mro
lookup. Therefore, our principle in programming is to use other methods instead of multiple inheritance as much as possible.
At this time, the Mixin
design pattern comes into play. The direct translation of Mixin
is "mix-in" or "supplement", which is a form of multiple inheritance. In multiple inheritance, the search order is based on the order of 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 can be seen from the above code, the Airplane
class implements multiple inheritance. In the inheritance chain, it inherits the Vehicle
class and the PlaneMixin
class. Here we follow the requirements of the Mixin
design pattern and add the suffix Mixin
to enhance the readability of the code.
The above code can be understood as follows: Airplane
is just a Vehicle
class, not a Plane
class, and the Mixin
suffix tells other readers that this class is added as a function to the subclass, not as a parent class. Its role is equivalent to interface
in Java.
In this way, we don't need a complex and large inheritance chain. We only need to choose the functionality of different classes to quickly construct the desired subclass.
3. Principles of Using the Mixin Design Pattern#
When using the Mixin
design pattern to achieve multiple inheritance, pay special attention to the following principles:
- First, the Mixin class must represent a certain functionality, not an object, which is the same as
Runnable
andCallable
in Java. - Secondly, it should have a single responsibility, if there are multiple functionalities, we should implement multiple
Mixin
classes. - Next, the Mixin class does not depend on the implementation of the subclass and belongs to an abstract class. It cannot be instantiated itself and cannot inherit classes other than Mixin.
- Finally, even if the subclass does not inherit the Mixin class, it must still work properly, only some functionality is missing and cannot be used.
Java interfaces only provide "specifications" for multiple inheritance. Mixin classes provide both "specifications" and "implementations" for multiple inheritance, making them easier to use than interfaces.
4. Supplement#
In other frameworks or languages, there are also similar Mixin features, such as Ruby
, Django
, Vue
, React
, and so on.