Как, используя полиморфизм, избавиться от диспетчеризации в Python?
На данный момент класс выглядит следующим образом:
from enum import Enum
# Три вспомогательных класса
class SensorType(Enum):
GYRO = 'gyro'
ACCL = 'accl'
class Axis(Enum):
X = 'x'
Y = 'y'
Z = 'z'
class SensorValue:
def __init__(self, value):
if not isinstance(value, (int, float)):
raise Exception("Введён не верный тип данных")
if not (-1 <= value <= 1):
raise Exception("Вышли за пределы допустимого интервала")
self._value = value
@property
def value(self):
return self._value
# Основной класс датчика
class Sensor:
def scale(self, sensor_type, axis, value):
if not (isinstance(sensor_type, SensorType) or isinstance(axis, Axis) \
or isinstance(value, SensorValue)):
raise Exception("Введён не верный тип данных")
if sensor_type == SensorType.GYRO:
if axis == Axis.X:
print('do something with SensorType = GYRO, axis = X')
elif axis == Axis.Y:
print('do something with SensorType = GYRO, axis = Y')
elif axis == Axis.Z:
print('do something with SensorType = GYRO, axis = Z')
elif sensor_type == SensorType.ACCL:
if axis == Axis.X:
print('do something with SensorType = ACCL, axis = X')
elif axis == Axis.Y:
print('do something with SensorType = ACCL, axis = Y')
elif axis == Axis.Z:
print('do something with SensorType = ACCL, axis = Z')
return f'{sensor_type}, {axis}, {value.value}'
Чтобы передать значения методу scale, его нужно вызывать следующим образом: sensor.scale(SensorType.GYRO, Axis.X, SensorValue(1)). Как, используя полиморфизм, упростить вызов до sensor.scale(GYRO, X, 1)?
Ответы (1 шт):
упростить вызов до sensor.scale(GYRO, X, 1)
так не получится и это не то, что имелось ввиду.
Речь о том, что можно избавится от явной проверки типов и использовать полиморфизм, чтобы реализовать разное поведение в зависимости от типа объекта. Это выгодно, если методов типа scale много и в каждом нужно реализовывать проверку типа. А если еще эти типы могут добавляться, то поддержка кода еще сильнее усложняется. В таком случае имеет смысл использовать полиморфизм. Ну а если scale это единственное место где у вас проверка на тип, то, наверно, смысла особого нет - усложнение может быть неоправданным.
Еще замечание по терминологии: это не избавление от диспечеризации (т.е. от направления потока выполнения в зависимости от условий), а использование другого механизма (а именно полиморфизма вместо явных проверок типа) для диспечеризации.
Если датчик один, но внутри себя содержит и гироскоп и акселерометр, то может иметь смысл отобразить это в коде:
class Gyro:
def scale(self, axis, value):
if not (isinstance(axis, Axis) or isinstance(value, SensorValue)):
raise Exception("Введён не верный тип данных")
if axis == Axis.X:
print('do something with gyro, axis = X')
elif axis == Axis.Y:
print('do something with gyro, axis = Y')
elif axis == Axis.Z:
print('do something with gyro, axis = Z')
class Accelerometr:
def scale(self, axis, value):
...
class Sensor:
def __init__(self):
self.gyro = Gyro()
self.accel = Accelerometr()
sensor.gyro.scale(Axis.X, SensorValue(0.5))
Теперь enum SensorType не нужен вообще. Он представлен в виде отдельных объектов классов с одинаковым интерфейсом (т.е. scale с одинаковой сигнатурой). И теперь у типа датчика есть не только, собственно тип, а еще и поведение в виде метода scale.
Если код усложняется, то некоторое общее поведение можно вынести в базовый класс:
class SensorSubComponent:
def scale(self, axis, value):
if not (isinstance(axis, Axis) or isinstance(value, SensorValue)):
raise Exception("Введён не верный тип данных")
if axis == Axis.X:
self.scale_x(value.value)
elif axis == Axis.Y:
self.scale_y(value.value)
elif axis == Axis.Z:
self.scale_z(value.value)
class Gyro(SensorSubComponent):
def scale_x(self, value):
print('do something with gyro, axis = X')
def scale_y(self, value):
print('do something with gyro, axis = Y')
def scale_z(self, value):
print('do something with gyro, axis = Z')
class Accelerometr(SensorSubComponent):
def scale_x(self, value):
print('do something with gyro, axis = X')
def scale_y(self, value):
print('do something with gyro, axis = Y')
def scale_z(self, value):
print('do something with gyro, axis = Z')
Опять же это имеет смысл, если код и логика усложняется.