在抽象/最终设计模式中如何封装一个具体子类
我在一个用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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。