在抽象/最终设计模式中如何封装一个具体子类

编程语言 2026-07-09

我在一个用Python编写的项目中使用 equinox 这个库。

这个库用于定义数据类,并强烈建议在设计这些类时使用 abstract/final pattern 模式。

我大致理解,但有一段我不太确定它的意思:

当你已经有一个具体的类,想对它做一点点微调时,该怎么办?在这种情况下,优先使用组合而非继承。编写一个包装器,按需转发每个方法。这同样具有表达力,并且意味着我们可以保持可读的类型安全规则。

因此举个具体的例子,假设我有以下的类结构:

from abc import abstractmethod
import jax.numpy as jnp
from jaxtyping import Float
import equinox as eqx

class AbstractShape(eqx.Module):

    area: eqx.AbstractVar[Float]

    @abstractmethod
    def compute_area(self):
        raise NotImplementedError

class Ellipse(AbstractShape):

    area: Float
    major_axis: Float
    minor_axis: Float

    def __init__(self, major_axis, minor_axis):
        self.major_axis = major_axis
        self.minor_axis = minor_axis
        self.area = self.compute_area()

    def compute_area(self):
        return jnp.pi * self.major_axis * self.minor_axis

# How to define a circle class according to the abstract/final pattern?
...

我直觉上想把 Circle 类写成 Ellipse 类的子类。

但根据推荐的模式,这是被禁止的;在抽象/最终设计模式中,永远不能对子类化具体类。

那么在这个情境下,[w]rite a wrapper that forwards each method as appropriate 到底是什么意思?我该如何透明地包装 Ellipse 以获得一个 Circle 类(例如通过提供半径参数)?

解决方案

你应该在你的 Circle 类中存放一个 Ellipse 的实例,并暴露相同的接口。

下面是让 Circle 类完美遵循Equinox的抽象/最终模式的方法:

class Circle(AbstractShape):
    # 1. State the fields required by the AbstractShape interface
    area: Float

    # 2. State the unique fields for Circle
    radius: Float

    # 3. Store the underlying concrete instance for composition
    _ellipse: Ellipse

    def __init__(self, radius: Float):
        self.radius = radius

        # Instantiate the concrete class internally
        self._ellipse = Ellipse(major_axis=radius, minor_axis=radius)

        # Forward the property from the internal instance
        self.area = self._ellipse.area

    # 4. Forward methods to the internal instance
    def compute_area(self):
        return self._ellipse.compute_area()

Circle 继承自 AbstractShape,而不是具体的 Ellipse

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章