过度使用mixin的危害及替代解决方案是什么?

8 投票
4 回答
3131 浏览
提问于 2025-04-16 04:36

有时候,使用 mixin 和多重继承可以帮助我们让代码更好用。

比如,下面这个设计

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

class User(FollowableMixin):
    ...

可能比直接把 get_followers 加到 User 上更容易重复使用:

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

因为以后我们可能还想支持其他可以被关注的对象,这些对象也能使用 get_followers

class BookStore(FollowableMixin):
    ...

不过,如果这种方式用得太多,代码可能会变得很复杂。

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

当有很多 mixin 类把属性和方法注入到你的类里时,理解代码就变得很困难。例如,你可能不知道你调用的方法是从哪里来的,而这个方法又可能会调用另一个 mixin 里的方法……

那我该怎么做才能简化这个程序呢?

4 个回答

5

拿出纸和笔,把那些实际会被创建的具体类写下来,比如用户(User)和书店(BookStore)。然后列出你希望这些类能执行的所有方法。只有看到这个列表,你才能理智地决定哪个类的结构最适合你的情况。手写的慢节奏可能会让你以新的方式思考对象之间的关系。试着详细地向一个想象中的朋友(或者我们!)解释你的类,这个朋友聪明但对你的问题一无所知。慢慢说出细节可能会让你有新的领悟。

混入类(Mixins)可以给你的项目带来很多通用性,让它能够扩展,但你常常需要在通用性(比较复杂)和实用性(通常更简单)之间做出妥协。四个混入类可以组合出2的4次方(即16)种具体类。如果在实际应用中,你的具体类远远少于这个数量,那么混入类可能就不是合适的工具。

如果你觉得通用性让你感到困惑,建议你退一步,先确定功能,然后用最简单的方式来实现这些功能。等到你有了一个可用的产品后,再考虑添加新功能,并在必要时进行重构。

6

有时候,把相关的功能放在一个类里会很有帮助,特别是当这些功能经常一起使用的时候。

class FooMixin(FollowableMixin, RunnableMixin):
    pass

这样,当你需要使用这些功能时,你只需要处理一两个基础类,而不是很多个。

当然,只有在这样做有意义的时候才应该这么做——不然可能会搞得很乱。如果不了解你具体的情况,很难判断这样做是否合适。

6

如果你的用户类有那么多特性,那可能说明你的应用程序比较复杂。比起从其他地方复制五个函数,拥有五个混入类要好得多。

这里有一些简化的建议:

  1. 你的用户类可能承担了太多功能。可以把它拆分成更小的类。

  2. 把一些混入类合并起来。例如,你可能会发现有五个类都是可以被关注、可以运行和可以飞的。可以创建一个中间类叫做FollowRunFly,它继承这三个混入类,然后在你的五个类中使用FollowRunFly。

  3. 也许你不需要把混入类切分得那么细。可以做一个大的混入类,在你的类中使用它,让代码在运行时决定对象是否可以飞或者被关注。

撰写回答