开发者

Overuse of mixin is evil and what are the alternative solutions?

Sometimes using mixin with multiple inheritance can help us improve reusability of our code.

For example, the following design

class FollowableMixin(object):
    def get_followers(self):
        ...
    ...

class User(FollowableMixin):
    ...

may be better reused than simply adding get_followers to User:

class User(object):
    def get_followers(self):
        ...
    ...

because later we may consider supporti开发者_如何学编程ng other followable entities that are potential clients of get_followers:

class BookStore(FollowableMixin):
    ...

However, if this pattern is overused, code may get too complex.

class User(FollowableMixin, RunnableMixin, FlyableMixin, WhatMixin ...):
    ...

With all these mixin classes injecting properties and methods to your class, it becomes very difficult to understand your code. For example, you don't know where the method you are calling is from, and this method may in turn include an invocation of a method in another mixin ...

What should I do to simplify this programme?


Sometimes it can help to collect together related features in a single class if they are often used together.

class FooMixin(FollowableMixin, RunnableMixin):
    pass

Then when you come to use it you only have one or two direct base classes instead of many.

Obviously you should only do this if it makes sense - it can be abused. Without knowing more about your specific example it is hard to know whether it makes sense to do this or not in your case.


If your User class really has that many characteristics that are appropriate, then you may simply have a complex application. Having five mixins is better than having five functions copied from other places.

Some possibilities for simplifying:

  1. Your User class is trying to do too much. Break it into smaller classes.

  2. Aggregate some of your mixins. For example, you may find there are five classes each of which is Followable and Runnable and Flyable. Makes an intermediate class FollowRunFly that derives from those three mixins, then use FollowRunFly in your five classes.

  3. Perhaps you don't have to slice your mixins so finely. Make one big mixin, and use it on your classes, and let the code determine at runtime whether the object can fly or be followed.


Use adapters instead of mixins. So in your case you would have an IFollowable interface and adapters from BookStore or User to IFollowable.

See http://ginstrom.com/scribbles/2009/03/27/the-adapter-pattern-in-python/ for one description of adapters in Python and in particular the comments from Martijn Faassen about using factories and interfaces and grokcore.component.


With paper and pencil, write down the concrete classes that actually get instantiated (e.g. User and BookStore). List all the methods that you want those classes to perform. Only by seeing this list can you rationally decide what class hierarchy best fits your situation. The slowness of writing by hand may force you to think about the relationships between your objects in a new way. Try explaining your classes in detail to an imaginary friend (or us!) who is smart but knows nothing about your problem. The slowness of articulating the details may lead to insight.

Mixins may give you a lot of generality to allow your project to grow, but you are often forced to make a compromise between generality (which is complex) and practicality (which is often simpler). Four mixins allows for 2**4 possible concrete classes. If in practice you have far fewer concrete classes, then mixins may not be the right tool for the job.

If you feel the generality is overwhelming you, I think it would be wise to back off the generality, freeze the features, and code in the simplest way that accommodates those features. Then, when you have a working product, you can think about adding new features, and refactor if necessary.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜