Noob组件设计问题

时间:2009-07-10 02:11:06

标签: python interface protocols

更新了问题,见下文

我正在开始一个新项目,我想尝试基于组件的架构(我选择PyProtocols)。这是一个显示和交互实时图形的小程序。

我从设计用户输入组件开始:

  • IInputDevice - 例如鼠标,键盘等...... InputDevice可能有一个或多个输出通道:
    • IOutput - 包含单个值的输出通道(例如MIDI滑块的值)
    • ISequenceOutput - 包含值序列的输出通道(例如,代表鼠标位置的2个整数)
    • IDictOutput - 包含命名值的输出通道(例如键盘每个键的状态,键盘符号索引)

现在我想定义接口来过滤那些输出(平滑,抖动,反转等......)。

我的第一种方法是创建一个InputFilter接口,它为连接到的每种输出通道都有不同的过滤方法......但PyProtocols文档中的介绍清楚地表明整个接口和适配器的东西是关于避免类型检查!

所以我的猜测是我的InputFilter接口应如下所示:

  • IInputFilter - 过滤IOutput
  • ISequenceInputFilter - 过滤器ISequenceOutput
  • IDictInputFilter - 过滤IDictOutput

然后我可以在I * Ouptut接口中使用connect()方法,它可以神奇地调整我的过滤器并使用适合输出类型的过滤器。

我试图实现它,它有点工作:

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重写了所有内容,它完美无缺。

1 个答案:

答案 0 :(得分:1)

我没有使用过PyProtocols,只使用了Zope组件架构,但它们足够相似,因为这些原则是相同的。

您的错误是您有两个适配器可以适应相同的事情。你们都有一个平均滤波器和一个反演滤波器。当您再询问过滤器时,两者都被找到,并且您收到“ambigous adapter”错误。

你可以通过使用不同的接口来平均滤波器和反相滤波器来解决这个问题,但它变得很愚蠢。在Zope组件架构中,您通常会使用命名适配器处理此案例。默认情况下,每个适配器都会获得一个名称。在这种情况下,您可以给适配器名称“平均”和“反转”,然后使用该名称查找它们,因此您知道是否获得平均值或反转滤波器。

对于更一般的问题,如果设计有意义,则很难说清楚。你有三种不同的输出和三种不同的过滤器似乎不是一个好主意。也许您可以将序列和字典输出组合成单值输出的组合,这样每个输出值都可以获得它自己的对象,因此可以单独过滤。这对我来说更有意义。