重构一个巨大的 Python class 使用继承来做组合

Refactoring a huge Python class using Inheritance to do Composition

几年前我制作了一个 pygame 游戏。它有效,但不是最好的编码风格,并且有很多 classic 代码的味道。我最近重新拾起它,这次我正尝试以更严格的方式重构它。

一个很大的代码味道是我有一个巨大的 class 游戏对象,它继承自 pygame.sprite.DirtySprite,其中有很多代码与以下内容相关:

我对 sprite 行为方式的思考越发疯狂,代码重复越积越多,更改也越来越困难。因此,我开始将功能分解为许多更小的 classes,然后在创建对象时将它们传入:

class GameObject(DirtySprite):
    def __init__(initial_position, mover_cls, imager_cls, exploder_cls):
        self.mover = mover(self, initial_position)
        self.imager = imager(self)
        self.exploder = exploder(self)

...
spaceship = GameObject(pos, CrazyMover, SpaceshipImager, BasicExploder)

随着我将越来越多的代码分解到这些帮助程序 classes 中,代码肯定更好、更灵活并且重复更少。然而,对于每种类型的助手 classes,参数的数量变得越来越长。创建精灵变成了一件苦差事,代码也很丑陋。因此,在另一个重构过程中,我创建了一堆非常小的 classes 来进行组合:

class GameObjectAbstract(MoverAbstract, ImagerAbstract, \
                         ExploderAbstract, DirtySprite):
    def __init__(self, initial_position):
        ...
...
class CrazySpaceship(CrazyMover, SpaceshipImager, BasicExploder, GameObjectAbstract):
    pass  # Many times, all the behavior comes from super classes

...
spaceship = CrazySpaceship(pos)

我更喜欢这种方法。这是一种常见的方法吗?将所有逻辑分解成小 classes 似乎具有相同的好处,但创建对象要清晰得多。

但是,这种方法不是动态的。例如,我不能在 运行 时间决定一个新的混搭。然而,这并不是我真正在做的事情。虽然我做了很多混搭,但似乎可以使用 class 语句静态定义它们。

关于未来的可维护性和重用性,我是否遗漏了什么?我听说组合比继承好,但这感觉就像我在用继承来做组合——所以我觉得这样还可以。

我应该使用其他模式吗?

没关系,如果你能足够好地分离行为 -

只是它根本不是 "composition" - 它是多重继承,使用我们所说的 "mixin classes":mixin class 大致是 class 提供可以与其他 classes.

组合的特定行为

如果您正确使用 Python 的 super,那可能是最好的方法。 (如果你设法创建你的游戏对象,基本上只是定义 class 名称和它使用的混合 classes,那实际上是一个很好的方法)

顺便说一下,如果你想在运行时使用此方法创建新的 classes,也可以 - 只需调用 type 来创建新的 [=31] =],而不是 class 语句:

class CrazySpaceship(CrazyMover, SpaceshipImager, BasicExploder, GameObjectAbstract):
    pass  # Many times, all the behavior comes from super classes

在 Python 中相当于:

CrazySpaceShip = type('CrazySpaceShip', (CrazyMover, SpaceshipImager, BasicExploder, GameObjectAbstract), {})

您用作第二个参数的元组可以是在运行时构建的任何序列。