In Part 1 we have seen how to define interfaces. Now we want to see how we can actually use them.

So let's look at the Document example. What we maybe need is a way to create new objects. Usually it's rather easy as you simply instantiate the class:

document = Document("title","body")

But imagine you have a lot of such content types and you want some registry of available content types. You could e.g. also have Image or Survey.

To do this we can define a new interface which all those types have in common: IContentType

This interface can be the superclass of e.g. IDocument:

class IContentType(Interface):
        """a content type"""

        title = Attribute("""the title of the object""")

        def render():
                """render this object"""

class IDocument(IContentType):
        """a documen content type"""
        body = Attribute("""the body of the object""")

As you can see we moved title and render() into the interface definition of IContentType as we defined that every content type should at least have a title and a render method. We simply add those type specific fields to those more specific content types (Image could have an image attribute).

Now we probably have some core Content Management component and we might also have additional (optional) packages which define additional content types. The core component needs to know about all of them so it can e.g. show them in a list. So what we need is a registry.

What we can do is to register a utility for each specific content type which knows how to create an instance. This could look as follows:

class DocumentFactory(object):
        """create a new document"""

        def create(self, title):
                return Document(title)


Let's turn this into a content type factory interface:

class IContentTypeFactory(Interface)
        """lots of documentation"""

        def create(title):
                """create a new instance of the content type and return it.
                Take title as only parameter.
                """

and we can add the implements(IContentTypeFactory) to the above class.

To make it a utility we register it:

from zope.component import getGlobalSiteManager
gsm = getGlobalSiteManager()

document_factory = DocumentFactory()
gsm.registerUtility(document_factory, IContentTypeFactory)

(This is one way of registering it in pure python. There are better and simpler ways to do that and I will describe them in a separate part).

Now we can retrieve the utility again:

from zope.component import getUtility
factory = getUtility(IContentTypeFactory)

document = factory.create("my title")

Now we have a document.

The only problem we still have is that we can only register one utility with that interface. What we want is many utilities (one for each content type) but with a different name. We call them named utilities.

We do the registration instead like this:

gsm.registerUtility(document_factory, IContentTypeFactory, 'document')

We simply add the name to the registration call and can then retrieve it like this:

factory = getUtility(IContentTypeFactory, 'document')

This was we tell the registry we want a utility which implements IContentTypeFactory (means we can call create()) and with the name "document" (so that we know that we get a Document back).

We can also retrieve a list of all utilities registered for this interface (because we of course don't know all the names in advance):

from zope.component import getUtilitiesFor
factories = getUtilitiesFor(IContentTypeFactory)

What we get is a list of tuples, consisting of the name and the actual utility, e.g.

[
        ('document', <DocumentFactory at ...>),
        ....
]

We can then iterate over these and maybe display the available content types.

In OGP this pattern can be useful to register handlers for each individual UDP packet we receive. It could work as follows:

1. receive packet
2. decode header and esp. the packet type (an numeric id)
3. look up the handler which knows what to do with that packet by the packet id:

        handler = getUtility(IPacketHandler, str(id))
4. call the handler's handle() method:
        result = handler.handle()

That way we can even have separate packages implementing additional handlers. We can also use queryUtility() instead of getUtility() which returns None instead of an exception of the handler is not found (because it might not be yet implemented).

Each handler then just needs to implement the handle() method and add a line

implements(IPacketHandler)

It also needs to register itself with the component architecture as shown above (registerUtility) but as said I will show easier ways to do that later (but it's still only one line).

Each handler could then be tested on it's own (e.g. by passing some premade packet into it) and it could even contain the doctest which tests it itself.

class SomePacketHandler(PacketHandler):
        """handle some packet

        >>> packet=test_packets.some_packet
        >>> handler = SomePacketHandler()
        >>> result = handler.handle(packet)
        >>> len(result)==155
        True

        >>> some other tests with results

        """
        implement(IPacketHandler)

        def handle(self, packet):
                """handle the packet"""
                ...


So all in all utilities are just some general functions which do a simple job and are registered globally so that every package/component can ask for them.

Another nice thing with named utilities (those which have a name additionally) is that you can define a default for those if no name if given.

E.g. if you register a utility like this

registerUtility(IUtility,utility)

then it has the name ""

If you then add named utilities like this:

registerUtility(IUtility,utility,"somename")

they are registered under "somename". So unnamed utilities are really just the same but their name is "".

This can be useful e.g. for the networking layer. You might want to have a utility which handles networking. The default one might simply use urllib2. But you can add additional named utilities which use eventlet etc. If you omit the name, you get the default, if you give one you get the named one.


I hope this is somewhat clear. The concept is actually rather simple: Remember a class instance with an interface and optionally a name and return it again if asked for it.


-- Christian






--
Christian Scholz                         video blog: http://comlounge.tv
COM.lounge                                   blog: http://mrtopf.de/blog
Luetticher Strasse 10                                    Skype: HerrTopf
52064 Aachen                              Homepage: http://comlounge.net
Tel: +49 241 400 730 0                           E-Mail [EMAIL PROTECTED]
Fax: +49 241 979 00 850                               IRC: MrTopf, Tao_T

neue Show: TOPFtäglich (http://mrtopf.de/blog/category/topf-taglich/)

_______________________________________________
Click here to unsubscribe or manage your list subscription:
https://lists.secondlife.com/cgi-bin/mailman/listinfo/pyogp

Reply via email to