如何正确组织一组班级?
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_frame
和 set_parameters
。
我想强制实现这些类/函数来为每个新对象保持相同的合成器,这样如果我想使用另一个相机,我可以保留我以前的脚本,我只需要创建一个新的 @987654330 @ 目的。 您还应该知道,这些类不会被直接实例化,而是可以构建的模式。
我查看了元类,在类中强制实现方法看起来不错,但我不确定:
你可以从一个元类BaseObject
创建一个元类(Camera
)
您可以强制实现元类中的类(如 BaseObject
中的 Actuator
)以及如何(通过 @abc.abstractmethod
?)
您可以在元类中强制实现类中的方法吗? (如get_frame()
)
这真的是唯一的方法还是我应该完全以不同的方式组织我的课程?
我应该回去做纸飞机吗? (请不要回答这个问题)
【问题讨论】:
为什么要嵌套类?您能否详细说明当前结构背后的想法? 您需要重新考虑您的设计。这基本上是不可测试的。 “所有对象都必须继承自 BaseObject,因此”。嵌套不是继承。你是什么意思?class Foo(object)
和 class Bar(Foo)
的标准继承模式在这里似乎可以正常工作。
但是你为什么要调用 classes 上的所有方法,而不是 instances? Sensor
和 Actuator
是 classes 还是 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()
方法——我的意思是,如果sensor
和actuator
的预期接口依赖于ObjectBase
子类,那么它就没有任何意义根本强制他们都有sensor
和actuator
属性。
@brunodesthuilliers:我不需要嵌套类,我只想正确组织我的方法以便于使用,这就是为什么我寻找更好的嵌套解决方案(但我不知道如何)。这同样适用于强制sensor
和actuator
,我想确保创建新对象的用户必须遵循相同的语法,并且我的所有对象都将具有可以分类为sensor
或actuator
的方法.
以哪种方式添加(到目前为止在技术上无用的)间接级别应该使“易于使用”? python.org/dev/peps/pep-0020【参考方案2】:
我想强制实现这些类/函数以保持 每个新对象的语法相同,因此如果我想使用 另一个相机,我可以保留我以前的脚本,我只需要创建 一个新的相机对象。
OT:您真的是指“对象”(=> 实例)还是子类?但无论如何:
既然您提到“Sensor”和“Actuator”对于不同的BaseObject
子类不一定具有相同的接口,所以我们暂时忽略整个BaseObject
部分并专注于Camera
部分。
如果我理解正确,你想要的是通用的Camera
类型。此类型必须具有 sensor
和 actuator
属性,它们都必须尊重给定的接口,但可能具有不同的实现。
在这一点上,我们(嗯,至少我)没有足够的上下文来决定我们是否需要抽象 BaseCamera
类型,或者我们是否只需要抽象 BaseCameraSensor
和 BaseCameraActuator
接口。可以肯定的是,我们确实需要BaseCameraSensor
和BaseCameraActuator
,所以让我们从这个开始。我假设传感器和执行器也需要知道它们所属的相机,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
现在我们可以处理“相机”部分了。如果实现的唯一变体部分封装在Sensor
和Actuator
中(这是策略模式的重点),那么我们不需要抽象基类——我们只需传递适当的Sensor
和Actuator
子类作为初始化器的参数:
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_class
和 actuator_class
的预期接口并在不尊重它们时让程序崩溃就足够了 - 这就是我们的方式我已经用 Python 编程多年了(就我而言,已经超过 15 年了)而且它是 JustWork(tm) - 但 ABC 至少使合同更清晰,程序会更快崩溃。但不要被愚弄:它不会让你的代码很安全(提示:什么都不会,句号)。
现在,如果我们有其他一些无法提前知道实现的变体部分,最简单的解决方案是遵循相同的模式 - 委托给在实例化时传递的 Strategy 对象。所以重点是:现在我们已经实现了这个,我们发现我们不需要一些BaseCamera
ABC。
【讨论】:
感谢您非常准确和详细的回答,这解释了很多。即使我不确定是否使用它,因为我似乎在创造一个非常复杂的东西,但我确实学到了很多东西,它肯定会帮助我改进我的工作。再次感谢您耐心解释这一切! 有些问题本来就是复杂的,需要复杂的解决方案,但“复杂”确实和“复杂”不是一回事。您可能想顺便阅读一下:blog.codinghorror.com/kiss-and-yagni以上是关于如何正确组织一组班级?的主要内容,如果未能解决你的问题,请参考以下文章