File: ComponentsAndInterfaces.stx

package info (click to toggle)
zope-devguide 20011206-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 456 kB
  • ctags: 45
  • sloc: python: 152; makefile: 124; sh: 54
file content (343 lines) | stat: -rw-r--r-- 13,500 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
Chapter 1: Components and Interfaces

  Zope is becoming a component system. Zope components will be Python
  objects with interfaces that describe them. Right now only some of
  the Zope code base uses interfaces. In coming releases more and more
  of Zope will include interfaces. As a Zope developer you can use
  interfaces right now to build your Zope components.

  Zope Components

    Components are objects that are associated with interfaces.  An
    interface is a Python object that describes how you work with
    other Python objects.  In this chapter, you'll see some simple
    examples of creating components, and a description of interfaces
    and how they work.

    Here is a very simple component that says hello.  Like all components,
    this one generally consists of two peices, an interface, and an
    implementation::

      from Interface import Base

      class Hello(Base):
          """ The Hello interface provides greetings. """

          def hello(self, name):
              """ Say hello to the name """

      class HelloComponent:

          __implements__ = Hello

          def hello(self, name):
              return "hello %s!" % name

    Let's take a look at this step by step.  Here, you see two Python class
    statements.  The first statement creates the *interface*, and the
    second statement creates the *implementation*.

    The first class statement creates the 'Hello' interface.  This
    interface describes one method, called 'hello'.  Notice that there is
    no implementation for this method, interfaces do not define behavior,
    they just describe a specification.

    The second 'class' statement creates the 'HelloComponent' class.
    This class is the actual component that *does* what 'Hello'
    *describes*.  This is usualy referred to as the *implementation*
    of 'Hello'.  In order for you to know what interfaces
    'HelloComponent' implements, it must somehow associate itself with
    an interface. The '__implements__' class attribute does just
    that. It says, "I implement these interfaces".  In this case,
    'HelloComponent' asserts that it implements one interface,
    'Hello'.

    The interface describes how you would work with the object, but it
    doesn't dictate how that description is implemented. For example,
    here's a more complex implementation of the 'Hello' interface::

      import xmlrpclib
      class XMLRPCHello:

          __implements__ = Hello

          def hello(self, name):
              """
              Delegates the hello call to a remote object using XML-RPC.
              """
              s = xmlrpclib.Server('http://www.zope.org/')
              return s.hello(name)

    This component contacts a remote server and gets its hello
    greeting from a remote component.

    And that's all there is to components, really.  The rest of this
    chapter describes interfaces and how you can work with them from
    the perspective of components.  In Chapter 3, we'll put all this
    together into a Zope product.

  Python Interfaces

    Interface describe the behavior of an object by containing useful
    information about the object.  This information includes:

      o Prose documentation about the object.  In Python terms, this is
        called the "doc string" of the interface.  In this element, you
        describe how the object works in prose language and any other
        useful information about the object.

      o Descriptions of attributes.  Attribute descriptions include the
        name of the attribute and prose documentation describing the
        attributes usage.

      o Descriptions of methods.  Method descriptions can include:

        o Prose "doc string" documentation about the method and its usage.

        o A sequence of parameter objects that describes the parameters
          expected by the method.

      o Optional tagged data.  Interface objects (and their
        attributes, methods, and method parameters) can have optional,
        application specific tagged data associated with them.
        Examples uses for this are security assertions, pre/post
        conditions, unit tests, and other possible information you may
        want to associate with an Interface or its attributes.

    Not all of this information is mandatory.  For example, you may only
    want the methods of your interface to have prose documentation and not
    describe the arguments of the method in exact detail.  Interface
    objects are flexible and let you give or take any of these
    components.

  Why Use Interfaces?

    Interfaces solve a number of problems that arise while developing
    large systems with lots of developers.

      o Developers waste a lot of time looking at the source code of your
        system to figure out how objects work.  This is even worse if
        someone else has already wasted their time doing the same thing.

      o Developers who are new to your system may misunderstand how your
        object works, causing, and possibly propagating, usage errors.

      o Because an object's interface is inferred from the source,
        developers may end up using methods and attributes that are meant
        for "internal use only".
 
      o Code inspection can be hard, and very discouraging to novice
        programmers trying to understand code written by gurus.

    Interfaces try to solve these problems by providing a way for you to
    describe how to use an object, and a mechanism for discovering that
    description.

  Creating Interfaces                                       

    The first step to creating a component, as you've been shown, is
    to create an interface.

    Interface objects can be conveniently constructed using the Python
    'class' statement.  Keep in mind that this syntax can be a little
    misleading, because interfaces are *not* classes.  It is important to
    understand that using Python's class syntax is just a convenience, and
    that the resulting object is an *interface*, not a class.

    To create an interface object using Python's class syntax, create a
    Python class that subclasses from 'Interface.Base'::

      from Interface import Base

      class Hello(Base):

          def hello(self, name):
              """ Say hello to the world """

    This interface does not implement behavior for its methods, it
    just describes an interface that a typical "Hello" object would
    realize.  By subclassing the 'Interface.Base' interface, the
    resulting object 'Hello' is an interface object. The Python
    interpreter confirms this::

      >>> Hello
      <Interface Hello at 812cbd4>

    Now, you can associate the 'Hello' Interface with your new,
    concrete class in which you define your user behavior. For
    example::

      class HelloComponent:

          __implements__ = Hello

          def hello(self, name):
              return "Hello %s!" % name

    This new class, 'HelloComponent' is a concrete class that
    implements the 'Hello' interface.  A class can realize more than one
    interface.  For example, say you had an interface called 'Item' that
    described how an object worked as an item in a "Container" object.  If
    you wanted to assert that 'HelloComponent' instances realized the
    'Item' interface as well as 'Hello', you can provide a sequence of
    Interface objects to the 'HelloComponent' class::

      class HelloComponent:

          __implements__ = Hello, Item

    This '__implements__' attribute is called an *interface assertion*.  An
    interface assertion can be either an interface, or a sequence of
    interface assertions. Here's a more complex example::

      class Sandwich:

          __implements__ = (Food, (Nourishing, Delicious), (GetsStaleQuickly, 
                           (EdibleWithHands, GoodForLunch)))

    Interface assertions allow complex nesting of interfaces. This is
    mostly useful when you wish to assert that your class implements
    some specific interfaces, along with whatever interfaces your base
    class implements::

      class Sandwhich(Food):

          __implements__ = (EdibleWithHands, GoodForLunch, Food.__implements__)

     Take care before you assert that your class implements the
     interfaces of your base classes.

  The Interface Model

    Interfaces can extend other interfaces.  For example, let's extend the
    'Hello' interface by adding an additional method::

      class SmartHello(Hello):
          """  A Hello object that remembers who it's greeted """

          def lastGreeted(self):
              """ Returns the name of the last person greeted. """

    'SmartHello' extends the 'Hello' interface.  It does this by using the
    same syntax a class would use to subclass another class.

    Now, you can ask the 'SmartHello' for a list of the interfaces it
    extends with 'getBases'::

      >>> SmartHello.getBases()
      [<interface Hello at 80c72c8>]

    An interface can extend any number of other interfaces, and
    'getBases' will return that list of interfaces for you.  If you
    want to know if 'SmartHello' extends any other interface,
    you could call 'getBases' and search through the list, but a
    convenience method called 'extends' is provided that returns true
    or false for this purpose::

      >>> SmartHello.extends(Hello)
      1
      >>> SmartHello.extends(Sandwich)
      0
      >>>

    Here you can see 'extends' can be used to determine if one interface
    extends another.

    You may notice a similarity between interfaces extending from
    other interfaces and classes sub-classing from other classes.
    This *is* a similar concept, but the two should not be considered
    equal.  There is no assumption that classes and interfaces exist
    in a one to one relationship; one class may implement several
    interfaces, and a class may not implement its base classes's
    interfaces.

    The distinction between a class and an interface should always be
    kept clear.  The purpose of a class is to share the implementation
    of how an object works.  The purpose of an interface is to
    document how to work *with* an object, not how the object is
    implemented.  It is possible to have several different classes
    with very different implementations realize the same interface.
    Because of this, interfaces and classes should never be confused.

  Querying an Interface

    Interfaces can be queried for information.
    The simplest case is to ask an interface the names of all
    the various interface items it describes.  From the Python interpreter,
    for example, you can walk right up to an interface and ask it for its
    *names*::

      >>> User.names()
      ['getUserName', 'getFavoriteColor', 'getPassword']

    Interfaces can also give you more interesting information about their
    items.  Interface objects can return a list of '(name, description)'
    tuples about their items by calling the *namesAndDescriptions* method.
    For example::

      >>> User.namesAndDescriptions()
      [('getUserName', <Interface.Method.Method instance at 80f38f0>),
      ('getFavoriteColor', <Interface.Method.Method instance at 80b24f0>),
      ('getPassword', <Interface.Method.Method instance at 80fded8>)]

    As you can see, the "description" of the Interface's three items
    in these cases are all 'Method' objects.  Descriptions objects can
    be either 'Attribute' or 'Method' objects.  Attributes, methods,
    and interface objects implement the following interface::

      'getName()' -- Returns the name of the object.
     
      'getDoc()' -- Returns the documentation for the object.

    Method objects provide a way to describe rich meta-data about Python
    methods. Method objects have the following methods:

      'getSignatureInfo()' -- Returns a dictionary describing the
      method parameters.

      'getSignatureString()' -- Returns a human-readable string
      representation of the method's signature.

    For example::

      >>> m=User.namesAndDescriptions()[0][1]
      >>> m
      <Interface.Method.Method instance at 80f38f0>
      >>> m.getSignatureString()
      '(fullName=1)'
      >>> m.getSignatureInfo()   
      {'varargs': None, 'kwargs': None, 'optional': {'fullName': 1}, 
      'required': (), 'positional': ('fullName',)}  

    You can use 'getSignatureInfo' to find out the names and types of
    the method parameters.

  Checking Implementation

    You can ask an interface if a certain class or instance that you hand
    it implements that interface.  For example, say you want to know if
    instances of the 'HelloComponent' class implement 'Hello'::

      Hello.implementedByInstancesOf(HelloComponent)

    This is a true expression.  If you had an instance of
    'HelloComponent', you can also ask the interface if that instance
    implements the interface::

      Hello.implementedBy(my_hello_instance)

    This would also return true if *my_hello_instance* was an instance of
    *HelloComponent*, or any other class that implemented the *Hello*
    Interface.

  Conclusion

    Interfaces provide a simple way to describe your Python
    objects. By using interfaces you document your objects'
    capabilities. As Zope becomes more component oriented, your
    objects will fit right in.  While components and interfaces are
    forward looking technologies, they are useful today for
    documentation and verification.