Обновленный вопрос, см. Ниже
Я начинаю новый проект и хотел бы поэкспериментировать с архитектурой на основе компонентов (я выбрал PyProtocols ). Это небольшая программа для отображения и взаимодействия с графикой в реальном времени.
Я начал с разработки компонентов пользовательского ввода:
- IInputDevice - например, мышь, клавиатура и т. д. ... У InputDevice может быть один или несколько выходных каналов:
- IOutput - выходной канал, содержащий одно значение (например, значение ползунка MIDI)
- ISequenceOutput - выходной канал, содержащий последовательность значений (например, 2 целых числа, представляющих положение мыши)
- IDictOutput - выходной канал, содержащий именованные значения (например, состояние каждой клавиши клавиатуры, проиндексированное символами клавиатуры)
Теперь я хотел бы определить интерфейсы для фильтрации этих выходных данных (сглаживание, джиттер, инвертирование и т. Д.).
Мой первый подход состоял в том, чтобы создать интерфейс InputFilter, который имел разные методы фильтрации для каждого типа выходного канала, к которому он был подключен ... Но введение в документацию PyProtocols ясно говорит о том, что весь интерфейс и адаптеры предназначены для предотвращения типа проверка!
Так что я предполагаю, что мои интерфейсы InputFilter должны выглядеть так:
- IInputFilter - фильтры IOutput
- ISequenceInputFilter - фильтры ISequenceOutput
- IDictInputFilter - фильтры IDictOutput
Тогда у меня мог бы быть метод connect () в интерфейсах I * Ouptut, который мог бы волшебным образом адаптировать мои фильтры и использовать тот, который подходит для типа вывода.
Я пытался реализовать это, и это отчасти работает:
class InputFilter(object):
"""
Basic InputFilter implementation.
"""
advise(
instancesProvide=[IInputFilter],
)
def __init__(self):
self.parameters = {}
def connect(self, src):
self.src = src
def read(self):
return self.src.read()
class InvertInputFilter(InputFilter):
"""
A filter inverting single values.
"""
def read(self):
return -self.src.read()
class InvertSequenceInputFilter(InputFilter):
"""
A filter inverting sequences of values.
"""
advise(
instancesProvide=[ISequenceInputFilter],
asAdapterForProtocols=[IInputFilter],
)
def __init__(self, ob):
self.ob = ob
def read(self):
res = []
for value in self.src.read():
res.append(-value)
return res
Теперь я могу адаптировать свои фильтры к типу вывода:
filter = InvertInputFilter()
single_filter = IInputFilter(filter) # noop
sequence_filter = ISequenceInputFilter(filter) # creates an InvertSequenceInputFilter instance
single_filter и sequence_filter имеют правильное поведение и генерируют одиночные и последовательные типы данных. Теперь, если я определю новый тип InputFilter для той же модели, я получу следующие ошибки:
TypeError: ('Ambiguous adapter choice', <class 'InvertSequenceInputFilter'>, <class 'SomeOtherSequenceInputFilter'>, 1, 1)
Я, должно быть, делаю что-то ужасно неправильное, мой дизайн даже правильный? Или, может быть, я упускаю из виду, как реализовать мой InputFilterS?
Обновление 2
Я понимаю, что ожидал здесь слишком много магии, адаптеры не проверяют тип объектов, которые они адаптируют, а просто смотрят на интерфейс, который они предоставляют, что сейчас звучит нормально для меня (помните, я новичок в этих понятиях) !).
Итак, я придумал новый дизайн (обрезанный до минимума и опущенный dict интерфейсы):
class IInputFilter(Interface):
def read():
pass
def connect(src):
pass
class ISingleInputFilter(Interface):
def read_single():
pass
class ISequenceInputFilter(Interface):
def read_sequence():
pass
Таким образом, IInputFilter теперь является своего рода универсальным компонентом, который фактически используется, ISingleInputFilter и ISequenceInputFilter предоставляют специализированные реализации. Теперь я могу написать адаптеры от специализированных к универсальным интерфейсам:
class SingleInputFilterAsInputFilter(object):
advise(
instancesProvide=[IInputFilter],
asAdapterForProtocols=[ISingleInputFilter],
)
def __init__(self, ob):
self.read = ob.read_single
class SequenceInputFilterAsInputFilter(object):
advise(
instancesProvide=[IInputFilter],
asAdapterForProtocols=[ISequenceInputFilter],
)
def __init__(self, ob):
self.read = ob.read_sequence
Теперь я пишу свой InvertInputFilter так:
class InvertInputFilter(object):
advise(
instancesProvide=[
ISingleInputFilter,
ISequenceInputFilter
]
)
def read_single(self):
# Return single value inverted
def read_sequence(self):
# Return sequence of inverted values
И чтобы использовать его с различными типами вывода, я бы сделал:
filter = InvertInputFilter()
single_filter = SingleInputFilterAsInputFilter(filter)
sequence_filter = SequenceInputFilterAsInputFilter(filter)
Но, опять же, это с треском проваливается с той же самой ошибкой, и на этот раз она запускается непосредственно определением InvertInputFilter:
TypeError: ('Ambiguous adapter choice', <class 'SingleInputFilterAsInputFilter'>, <class 'SequenceInputFilterAsInputFilter'>, 2, 2)
(ошибка исчезает, как только я добавляю ровно один интерфейс в предложение instancesProvide класса)
Обновление 3
После некоторого обсуждения списка рассылки PEAK кажется, что эта последняя ошибка вызвана недостатком дизайна в PyProtocols, который выполняет некоторые дополнительные проверки во время объявления. Я переписал все с zope.interface, и он отлично работает.