I am currently developing a new driver system for physical experiments. The system will consist of several drivers for electronical instruments that can be very different from each other. But all drivers are made up mutliple channels, that are used for different things.
To keep the system uniform, I decided to define base classes for the driver and for the channels. Let us call them DriverBase
and ChannelBase
. All driver implementations are based on these base classes.
For example: To develop a driver for instrument XY, one needs to implement the classes XYDriver
and XYChannel
and for the instrument AB the classes ABDriver
and ABChannel
are needed. To make the system foolproof I want to use type hints, so nobody would try to use incompatible types. So the base classes would look like this:
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
Now I can be sure that a DriverBase
has a list of ChannelBase
s and each ChannelBase
has a DriverBase
as parent object. This works pretty fine and my IDE will remind me, if something is wrong implemented. But because I want to make the system foolproof and I am little perfectionist, I want to keep this behaiviour in the subclasses, too, without redefining the channels
and parent_device
properties, because there are actually some more than those two properties. I need to prevent the users from mixing up the subclasses, like using XYChannel
s in ABDriver
s or ABChannel
s in XYDriver
s, as seen in ABDriver
. This is what I want to avoid. The easiest way is to make those properties abstract and force the users to make new type hints in the overridden properties. But this is not what I want... I am searching for a solution that is completely located in the base classes. I hope there is some kind of design pattern, that holds relating classes together and prevents them from interacting with classes outside this relations.
Do you guys have any idea about this? I hope someone understands what I mean, because it seems to be very specific and it is really hard to explain for me.
Thanks in advance.
Aucun commentaire:
Enregistrer un commentaire