I'm trying to get my head around the Mixin concept but I can't seem to understand what it is. The way I see it is that it's a way to expand the capabilities of a class by using inheritance. I've read that people refer to them as "abstract subclasses". Can anyone explain why?
I'd appreciate if you'd explain your answer based on the following example (From one of my lecture slideshows):
What is a Functional Mixin? Functional mixins are composable functions which mix new properties or behaviors with properties from a given object. Functional mixins don't depend on or require a base factory or constructor: Simply pass any arbitrary object into a mixin, and it will be extended.
Mixins are sometimes described as being "included" rather than "inherited". In short, the key difference from an inheritance is that mix-ins does NOT need to have a "is-a" relationship like in inheritance. From the implementation point of view, you can think it as an interface with implementations.
Mixins are an alternative class design pattern that avoids both single-inheritance class fragmentation and multiple-inheritance diamond dependencies. A mixin is a class that defines and implements a single, well-defined feature. Subclasses that inherit from the mixin inherit this feature—and nothing else.
Mixin is a way properties are added to objects without using inheritance. For example, let's say we have a Person class. And we want people to be able to say Hi. We can create a sayHiMixin and use it to make People say hi −
Before going into what a mix-in is, it's useful to describe the problems it's trying to solve. Say you have a bunch of ideas or concepts you are trying to model. They may be related in some way but they are orthogonal for the most part -- meaning they can stand by themselves independently of each other. Now you might model this through inheritance and have each of those concepts derive from some common interface class. Then you provide concrete methods in the derived class that implements that interface.
The problem with this approach is that this design does not offer any clear intuitive way to take each of those concrete classes and combine them together.
The idea with mix-ins is to provide a bunch of primitive classes, where each of them models a basic orthogonal concept, and be able to stick them together to compose more complex classes with just the functionality you want -- sort of like legos. The primitive classes themselves are meant to be used as building blocks. This is extensible since later on you can add other primitive classes to the collection without affecting the existing ones.
Getting back to C++, a technique for doing this is using templates and inheritance. The basic idea here is you connect these building blocks together by providing them via the template parameter. You then chain them together, eg. via typedef
, to form a new type containing the functionality you want.
Taking your example, let say we want to add a redo functionality on top. Here's how it might look like:
#include <iostream> using namespace std; struct Number { typedef int value_type; int n; void set(int v) { n = v; } int get() const { return n; } }; template <typename BASE, typename T = typename BASE::value_type> struct Undoable : public BASE { typedef T value_type; T before; void set(T v) { before = BASE::get(); BASE::set(v); } void undo() { BASE::set(before); } }; template <typename BASE, typename T = typename BASE::value_type> struct Redoable : public BASE { typedef T value_type; T after; void set(T v) { after = v; BASE::set(v); } void redo() { BASE::set(after); } }; typedef Redoable< Undoable<Number> > ReUndoableNumber; int main() { ReUndoableNumber mynum; mynum.set(42); mynum.set(84); cout << mynum.get() << '\n'; // 84 mynum.undo(); cout << mynum.get() << '\n'; // 42 mynum.redo(); cout << mynum.get() << '\n'; // back to 84 }
You'll notice I made a few changes from your original:
value_type
for the second template param to make its usage less cumbersome. This way you don't have to keep typing <foobar, int>
everytime you stick a piece together.typedef
is used.Note that this is meant to be a simple example to illustrate the mix-in idea. So it doesn't take into account corner cases and funny usages. For example, performing an undo
without ever setting a number probably won't behave as you might expect.
As a sidenote, you might also find this article helpful.
A mixin is a class dessigned to provide functionality for another class, normally through a specified class which provides the basic features that the functionality needs. For example, consider your example:
The mixin in this case provides the functionality of undoing the set operation of a value class. This hability is based on the get/set
functionality provided by a parametrized class (The Number
class, in your example).
Another example (Extracted from "Mixin-based programming in C++"):
template <class Graph> class Counting: public Graph { int nodes_visited, edges_visited; public: Counting() : nodes_visited(0), edges_visited(0), Graph() { } node succ_node (node v) { nodes_visited++; return Graph::succ_node(v); } edge succ_edge (edge e) { edges_visited++; return Graph::succ_edge(e); } ... };
In this example, the mixin provides the functionality of counting vertices, given a graph class that performs trasversal operations.
Commonly, in C++ mixins are implemented through the CRTP idiom. This thread could be a good read about a mixin implementation in C++: What is C++ Mixin-Style?
Here is an example of a mixin that takes advantage of the CRTP idiom (Thanks to @Simple):
#include <cassert> #ifndef NDEBUG #include <typeinfo> #endif class shape { public: shape* clone() const { shape* const p = do_clone(); assert(p && "do_clone must not return a null pointer"); assert( typeid(*p) == typeid(*this) && "do_clone must return a pointer to an object of the same type" ); return p; } private: virtual shape* do_clone() const = 0; }; template<class D> class cloneable_shape : public shape { private: virtual shape* do_clone() const { return new D(static_cast<D&>(*this)); } }; class triangle : public cloneable_shape<triangle> { }; class square : public cloneable_shape<square> { };
This mixin provides the functionality of heterogeneous copy to a set (hierarchy) of shape classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With