如何正确组织一组班级?

Posted

技术标签:

【中文标题】如何正确组织一组班级?【英文标题】:How to organize a set of classes properly? 【发布时间】:2015-05-21 14:19:53 【问题描述】:

我想通过继承创建一组对象,我需要一些建议来组织我的实现。

所有对象都必须继承自 BaseObject,如下所示:

class BaseObject:
    class Actuator:
        pass
    class Sensor:
        pass

我想确保从BaseObject 继承的所有对象都强制拥有一个类Actuator 和一个类Sensor

然后我有另一个抽象级别,带有一些泛型对象,例如:

class Camera(BaseObject):
    class Actuator:
        pass
    class Sensor:
        def get_frame():
            pass
        def set_parameter():
            pass

而且我希望我创建的每个 Camera 对象至少有 get_frameset_parameters

我想强制实现这些类/函数来为每个新对象保持相同的合成器,这样如果我想使用另一个相机,我可以保留我以前的脚本,我只需要创建一个新的 @987654330 @ 目的。 您还应该知道,这些类不会被直接实例化,而是可以构建的模式。

我查看了元类,在类中强制实现方法看起来不错,但我不确定:

你可以从一个元类BaseObject创建一个元类(Camera) 您可以强制实现元类中的类(如 BaseObject 中的 Actuator)以及如何(通过 @abc.abstractmethod ?) 您可以在元类中强制实现类中的方法吗? (如get_frame()) 这真的是唯一的方法还是我应该完全以不同的方式组织我的课程? 我应该回去做纸飞机吗? (请不要回答这个问题)

【问题讨论】:

为什么要嵌套类?您能否详细说明当前结构背后的想法? 您需要重新考虑您的设计。这基本上是不可测试的。 “所有对象都必须继承自 BaseObject,因此”。嵌套不是继承。你是什​​么意思? class Foo(object)class Bar(Foo) 的标准继承模式在这里似乎可以正常工作。 但是你为什么要调用 classes 上的所有方法,而不是 instancesSensorActuatorclasses 还是 methods?那不应该是Camera(whatever, it, needs).sensor.get_frame()吗?这些类将如何使用? 【参考方案1】:

我会这样实现它们:

from abc import ABCMeta

class BaseObject:
    __metaclass__ = ABCMeta
    def __init__(self):
        self.actuator = self._get_actuator()
        self.sensor = self._get_sensor()
    @abstractmethod
    def _get_actuator(self):
        return 'actuator'
    @abstractmethod
    def _get_sensor(self):
        return 'sensor'

每个子类都有一个执行器和一个传感器参数。如果您在子类中覆盖 __init__,请确保调用 super.__init__

Class Sensor:
    __metaclass__ = ABCMeta
    pass

class CameraSensor(Sensor):
    __metaclass__ = ABCMeta
    @abstractmethod
    def get_frame():
        pass
    @abstractmethod
    def set_parameter():
        pass

class Camera(BaseObject):
    def _get_sensor(self):
        # get the sensor
        assert issubclass(sensor, CameraSensor)

【讨论】:

据我了解 OP 的要求,get_frame() 应该是 Sensor ABC 的一部分 - 我的意思是,所有 Sensor 子类都应该有这个方法。另外,Camera._get_sensor() should obviously return a CameraSensor` 实例(我假设这就是你的意思,但它实际上失败了),我会在 BaseObject._get_sensor()BaseObject._get_camera() 中提出 NotImplementedError,而不是返回不兼容的类型。 @brunodesthuilliers:事实上,所有对象都应该有一个Sensor 和一个Actuator 类(作为BaseObject 的子级),但只有Camera(它也是@ 的子级) 987654336@) 在Sensor 类中应该有一个get_frame() 方法 @HarryPotfleur:第一点:我仍然不明白你为什么坚持嵌套类 - 你想要的是你所有的 BaseObject 子类都有一个 sensor 和一个 actuator 属性,但是这些属性没有理由成为类,并且有很多理由让它们成为类。第二点:如果只有Camera.sensor 应该有一个get_frame() 方法——我的意思是,如果sensoractuator 的预期接口依赖于ObjectBase 子类,那么它就没有任何意义根本强制他们都有sensoractuator属性。 @brunodesthuilliers:我不需要嵌套类,我只想正确组织我的方法以便于使用,这就是为什么我寻找更好的嵌套解决方案(但我不知道如何)。这同样适用于强制sensoractuator,我想确保创建新对象的用户必须遵循相同的语法,并且我的所有对象都将具有可以分类为sensoractuator 的方法. 以哪种方式添加(到目前为止在技术上无用的)间接级别应该使“易于使用”? python.org/dev/peps/pep-0020【参考方案2】:

我想强制实现这些类/函数以保持 每个新对象的语法相同,因此如果我想使用 另一个相机,我可以保留我以前的脚本,我只需要创建 一个新的相机对象。

OT:您真的是指“对象”(=> 实例)还是子类?但无论如何:

既然您提到“Sensor”和“Actuator”对于不同的BaseObject 子类不一定具有相同的接口,所以我们暂时忽略整个BaseObject 部分并专注于Camera 部分。

如果我理解正确,你想要的是通用的Camera 类型。此类型必须具有 sensoractuator 属性,它们都必须尊重给定的接口,但可能具有不同的实现。

在这一点上,我们(嗯,至少我)没有足够的上下文来决定我们是否需要抽象 BaseCamera 类型,或者我们是否只需要抽象 BaseCameraSensorBaseCameraActuator 接口。可以肯定的是,我们确实需要BaseCameraSensorBaseCameraActuator,所以让我们从这个开始。我假设传感器和执行器也需要知道它们所属的相机,FWIW 真的尖叫“策略”模式,所以我们从一个基类开始——我们称之为“BaseStrategy”——除了得到一个引用它的宿主对象:

class Strategy(object):
    def __init__(self, parent):
        self._parent = parent

现在让我们定义我们的“CameraSensor”和“CameraActuator”接口:

class BaseCameraSensor(Strategy):
    __metaclass__ = ABCMeta

    @abstractmethod
    def get_frame(self):
        raise NotImplementedError

    @abstractmethod
    def set_parameter(self, value):
        raise NotImplementedError

class BaseCameraActuator(Strategy):
    __metaclass__ = ABCMeta

    @abstractmethod
    def random_method(self):
        raise NotImplementedError

现在我们可以处理“相机”部分了。如果实现的唯一变体部分封装在SensorActuator 中(这是策略模式的重点),那么我们不需要抽象基类——我们只需传递适当的SensorActuator 子类作为初始化器的参数:

class Camera(object):
    def __init__(self, brand, model, sensor_class, actuator_class):
        self.brand = brand
        self.model = model

        assert issubclass(sensor_class, BaseCameraSensor)
        self.sensor = sensor_class(self)

        assert issubclass(actuator_class, BaseCameraActuator)
        self.actuator = actuator_class(self)

问题就解决了。 FWIW,请注意,您实际上并不需要 Strategy 类或 ABC 部分,只需记录 sensor_classactuator_class 的预期接口并在不尊重它们时让程序崩溃就足够了 - 这就是我们的方式我已经用 Python 编程多年了(就我而言,已经超过 15 年了)而且它是 JustWork(tm) - 但 ABC 至少使合同更清晰,程序会更快崩溃。但不要被愚弄:它不会让你的代码很安全(提示:什么都不会,句号)。

现在,如果我们有其他一些无法提前知道实现的变体部分,最简单的解决方案是遵循相同的模式 - 委托给在实例化时传递的 Strategy 对象。所以重点是:现在我们已经实现了这个,我们发现我们不需要一些BaseCamera ABC。

【讨论】:

感谢您非常准确和详细的回答,这解释了很多。即使我不确定是否使用它,因为我似乎在创造一个非常复杂的东西,但我确实学到了很多东西,它肯定会帮助我改进我的工作。再次感谢您耐心解释这一切! 有些问题本来就是复杂的,需要复杂的解决方案,但“复杂”确实和“复杂”不是一回事。您可能想顺便阅读一下:blog.codinghorror.com/kiss-and-yagni

以上是关于如何正确组织一组班级?的主要内容,如果未能解决你的问题,请参考以下文章

组织孩子的 Qt Quick2 自定义控件

Python Argparse,如何正确组织 ArgParse 代码

如何正确组织我的继承类以利用多态性?

如何正确组织 res 文件夹中的资源? [复制]

模块与命名空间:组织大型打字稿项目的正确方法是啥?

如何将命令arg正确地拆分为有组织的数组? [关闭]