Mixin way?
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Wed Apr 3 11:47:20 EDT 2013
On Wed, 03 Apr 2013 15:04:51 +0100, andrea crotti wrote:
> I have some classes that have shared behaviours, for example in our
> scenario an object can be "visited", where something that is visitable
> would have some behaviour like
[snip mixins]
By the way, it's a common convention to name mixin classes as "SpamMixin".
> So now I'm not sure how to use it though.
> One way would be multiple subclasses
>
> class MyObjectBase(object):
> pass
>
> class MyObj(MyObjectBase, Visitable):
> pass
>
> for example.
What's the purpose of MyObjectBase? Either your example is too simple, or
it actually has no purpose, and you should simply write:
class MyObj(object, VisitableMixin):
# code goes here
instead of having one extra layer in the inheritance hierarchy.
> This solution is probably easy, but at the same time disturbing because
> MyObjectBase is semantically quite different from Visitable, so
> subclassing from both seems wrong..
Not really. From what you describe, this seems to be exactly the use-case
for mixins. Yes, mixins are by definition somewhat of an abuse of Object-
Oriented concepts. Mixins don't so much represent a "type of thing" as a
convenient bundle of encapsulated behaviour and/or state.
E.g. MyObj represents some sort of "MyObj thing" (or at least it
*should*, if your OO classes are well-planned), but Visitable(Mixin) does
not represent a thing at all.
But here's a way to look at it to justify the concept of mixins. Think of
regular classes and subclasses as representing trees of descent, like
biological groups:
Animal -> Mammal -> Rodent -> Porcupine
Animal -> Monotreme -> Echidna
Then mixins represent convergent evolution of some trait:
Animal -> Mammal -> Rodent + QuillsMixin -> Porcupine
Animal -> Monotreme + QuillsMixin -> Echidna
> The other solution (which is what is partially done now) is to use
> another class attribute:
>
> class ObjectWithMixin(CouchObject):
> MIXINS = [Visitable]
>
> and then do all the smart things needed:
> - at object construction time
> - when setting attributes and so on..
>
> This solution is more complicated to implement but maybe is more
> flexible and more "correct", what do you think?
I think that's worse.
You seem to have *almost* stumbled onto the design pattern known as
"composition" or "delegation", only not quite.
Normal class-based design models a "is-a" relationship. "Lassie is a
Dog", so we would do:
lassie = Dog()
Composition models a "has-a" relationship. For example, both cars and
boats have engines, so we might be tempted to use a mixin:
class Car(EngineMixin):
pass
class Boat(EngineMixin):
pass
which isn't an awful solution. But a conceptually cleaner solution might
be to do this:
class Car:
def __init__(self):
self.engine = Engine()
def go(self):
print "Fasten your seat belt"
self.engine.start()
self.handbrake = False
self.change_gear("drive")
self.accelerate()
class Boat:
def __init__(self):
self.engine = Engine()
def go(self): ...
and then both cars and boats can *delegate* behaviour to the engine
object.
So, if you think of "Visitable" as a gadget that can be strapped onto
your MyObj as a component, then composition is probably a better design.
But if you think of "Visitable" as a mere collection of behaviour and
state, then a mixin is probably a better design.
--
Steven
More information about the Python-list
mailing list