Before moving on to what mixing is, it is helpful to describe the problems that he is trying to solve. Say that you have a bunch of ideas or concepts that you are trying to model. They can be connected in some way, but for the most part they are orthogonal, that is, they can stand independently independently of each other. Now you can model this by inheritance, and each of these concepts derives from some general interface class. You then provide specific methods in a derived class that implements this interface.
The problem with this approach is that this design does not offer a clear, intuitive way to take each of these specific classes and combine them.
The idea with mix-ins is to provide a bunch of primitive classes where each of them models a basic orthogonal concept and can combine them together to create more complex classes with just the function you want - kind of like legos. The primitive classes themselves are intended to be used as building blocks. This is extensible since you can later add other primitive classes to the collection without affecting existing ones.
Returning to C ++, for this you need to use templates and inheritance. The main idea here is that you connect these building blocks together by providing them through a template parameter. Then you connect them together, for example. via typedef to form a new type containing the desired functionality.
Taking your example, suppose we want to add redo functionality on top. Here's what 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 will notice that I made a few changes from your original:
- Virtual functions are not really needed here because we know for sure that our composite class type is at compile time.
- I added a default
value_type for the second template parameter to make its use less cumbersome. This way you do not need to type <foobar, int> every time you insert a piece together. - Instead of creating a new class that inherits from parts, a simple
typedef .
Note that this should be a simple example illustrating the idea of mixing. Therefore, it does not take into account corner cases and funny customs. For example, executing undo without setting the number will probably not behave as you might expect.
As a side element, you can also find this article .
greatwolf Sep 12 '13 at 22:21 2013-09-12 22:21
source share