相关基类的设计模式

2024-10-03 11:19:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在为物理实验开发一个新的驱动系统。该系统将由多个电子仪器驱动器组成,这些驱动器之间可能有很大差异。但是所有的驱动程序都是由多个通道组成的,这些通道用于不同的事情

为了保持系统的一致性,我决定为驱动程序和通道定义基类。让我们称之为DriverBaseChannelBase。所有驱动程序实现都基于这些基类

例如:要为工具XY开发驱动程序,需要实现类XYDriverXYChannel,对于工具AB,需要类ABDriverABChannel。 为了使系统万无一失,我想使用类型提示,因此没有人会尝试使用不兼容的类型。基类如下所示:

class DriverBase:
    def __init__(self):
        # This list needs to be type-hinted, so the derived class only accepts a
        # specific channel-type, that is related to this specific driver
        # implementation. If the subclass tries to append another channel-type
        # than expected, the user should be informed about this.
        self._channels: List[ChannelBase] = [] 

    # The users should know, what is returned by this property as exactly as
    # possible.
    @property
    def channels(self) -> ChannelBase:
        return self._channels

class ChannelBase:
    def __init__(self, parent: DriverBase):
        self._parent = parent

    # The users should know, what is returned by this property as exactly as
    # possible.
    @property
    def parent_device(self) -> DriverBase:
        return self._parent

# The subclasses should be as simple as possible, because those are
# implemented by the users themselves.

class XYChannel(ChannelBase):
    # ...
class ABChannel(ChannelBase):
    # ...

class XYDriver(DriverBase):
    def __init__(self, channel_count: int):
        super().__init__()
        # This is how it should be used
        for i in range(channel_count):
            self._channels.append(XYChannel(i))

class ABDriver(DriverBase):
    def __init__(self, channel_count: int):
        super().__init__()
        # Here a user made a typical copy-paste-mistake. Maybe the channels
        # of both devices are very similar so that the script will terminate
        # without any error, but the instrument channels did not get any
        # data. So it would be very nice, if the IDE automatically warns the
        # user, that this type is not allowed.
        for i in range(channel_count):
            self._channels.append(XYChannel(i)) # ABChannel would be correct

现在我可以确定一个DriverBase有一个ChannelBase列表,每个ChannelBase都有一个DriverBase作为父对象。这工作很好,我的IDE会提醒我,如果有什么是错误的。 但是,因为我想让系统万无一失,而且我有点完美主义,所以我也希望在子类中保持这种行为,而不必重新定义channelsparent_device属性,因为实际上还有比这两个属性更多的属性。我需要防止用户混淆子类,比如在ABDriver中使用XYChannel或在XYDriver中使用ABChannel,如ABDriver所示。这就是我想避免的。最简单的方法是将这些属性抽象化,并强制用户在重写的属性中创建新的类型提示。但这不是我想要的。。。我正在寻找一个完全位于基类中的解决方案。我希望有某种设计模式,将相关的类保持在一起,并防止它们与此关系之外的类进行交互

你们知道这件事吗?我希望有人能理解我的意思,因为它看起来很具体,很难为我解释

提前谢谢


更新

这个问题背后的想法如下。我的用户(物理学家和技术人员)使用我的驱动程序框架来自动化他们的测量,他们还将开发自己的驱动程序。我将使框架尽可能简单,对所有用户提供的参数进行自定义异常和类型/值检查。但事实并非如此。。。我想要的不是必需品。但如果用户在编写代码时收到警告,那就太好了。因此,他们不必等到可以测试代码时才知道代码是否正常工作


Tags: theselfinit系统defas驱动程序channel
1条回答
网友
1楼 · 发布于 2024-10-03 11:19:45

你的用户会写代码吗??您已经在基类中定义了接口,不确定要实现什么,但可以在中添加isinstance()类型的检查

for i in range(channel_count):
    if not isinstance(ChannelBase, XYChannel(i)):
        raise IncompatibleChannelException('your message here')

    self._channels.append(XYChannel(i))

(在另一个中相同,但交换类)。或者像你说的那样,让他们抽象,这样他们就知道他们实现了正确的类。 如果您想遵循这个建议,可以在初始化时向typecheck添加一个特定的方法(在init执行期间调用)

有点像

    def __init__(self, channel_class):
        # This list needs to be type-hinted, so the derived class only accepts a
        # specific channel-type, that is related to this specific driver
        # implementation. If the subclass tries to append another channel-type
        # than expected, the user should be informed about this.
        self._channels: List[ChannelBase] = [] 
        self.channel_class = channel_class
        self.type_check_on_init()
        for i in range(channel_count):
            self._channels.append(self.channel_class(i))

    def type_check_on_init(self):
        if not isinstance(ChannelBase, self.channel_class):
            raise IncompatibleChannelException('Channel must be of type {} not {}'.format(ChannelBase, self.channel_class)

如果ABDriver始终与ABChannel一起工作,则可以使它们抽象类的属性并强制人们实现它们:

class DriverBase:
    def __init__(self):
        # This list needs to be type-hinted, so the derived class only accepts a
        # specific channel-type, that is related to this specific driver
        # implementation. If the subclass tries to append another channel-type
        # than expected, the user should be informed about this.
        self._channels: List[ChannelBase] = [] 

    # The users should know, what is returned by this property as exactly as
    # possible.
    @abc.abstractproperty
    def channel_class(self):
        pass

这样,每个子类都必须指定它们可以实现的通道类

相关问题 更多 >