##language:zh
'''
Finger演化:生成finger库

The Evolution of Finger: making a finger library
'''
-- dreamingk [<<DateTime(2004-08-09T02:16:43Z)>>]
<<TableOfContents>>

== 介绍(Introduction) ==
这是Twisted教程《Twisted入门 - Finger演化》的第十部分。

This is the tenth part of the Twisted tutorial Twisted from Scratch, or The Evolution of Finger.

在这个部分中,我们把装载finger服务的应用代码从定义finger服务的库代码中分离出来,将应用放在Twisted应用配置(Twisted Application Configuration)(.tac)文件中。我们同样将配置(象 HTML 模板)放入到分离的文件中。

In this part, we separate the application code that launches a finger service from the library code which defines a finger service, placing the application in a Twisted Application Configuration (.tac) file. We also move configuration (such as HTML templates) into separate files.

== 组织(Organization) ==
现在这些代码,尽管设计良好并且相当模块化,但组织得不合适。在应用之上的所有东西应归于一个模块,所有的 HTML 模板应归于分离的文件。

Now this code, while quite modular and well-designed, isn't properly organized. Everything above the application belongs in a module, and the HTML templates all belong in separate files. 

我们可以使用 templateFile 和 templateDirectory属性来指明,对每个页面使用什么 HTML 模板文件,并且到哪里去寻找。

We can use the templateFile and templateDirectory attributes to indicate what HTML template file to use for each Page, and where to look for it.
{{{
#!python
# organized-finger.tac
# eg:  twistd -ny organized-finger.tac

import finger

from twisted.internet import protocol, reactor, defer
from twisted.spread import pb
from twisted.web import resource, server
from twisted.application import internet, service, strports
from twisted.python import log

application = service.Application('finger', uid=1, gid=1)
f = finger.FingerService('/etc/users')
serviceCollection = service.IServiceCollection(application)
internet.TCPServer(79, finger.IFingerFactory(f)
                   ).setServiceParent(serviceCollection)

site = server.Site(resource.IResource(f))
internet.TCPServer(8000, site
                   ).setServiceParent(serviceCollection)

internet.SSLServer(443, site, ServerContextFactory()
                   ).setServiceParent(serviceCollection)

i = finger.IIRCClientFactory(f)
i.nickname = 'fingerbot'
internet.TCPClient('irc.freenode.org', 6667, i
                   ).setServiceParent(serviceCollection)

internet.TCPServer(8889, pb.PBServerFactory(finger.IPerspectiveFinger(f))
                   ).setServiceParent(serviceCollection)
}}}
Source listing - listings/finger/organized-finger.tac
Note that our program is now quite separated. We have: 

Code (in the module)
Configuration (file above)
Presentation (templates)
Content (/etc/users)
Deployment (twistd)
原型并不需要这种层次的分离,因此早先的例子全都绑在了一起。然而,真正的应用需要。要感谢的是,如果我们编写的代码正确,使各部分很好的分离是容易的。

Prototypes don't need this level of separation, so our earlier examples all bunched together. However, real applications do. Thankfully, if we write our code correctly, it is easy to achieve a good separation of parts. 
== 容易的配置(Easy Configuration) ==
我们也可以利用makeService方法为通常的情况提供容易的配置,后面这个方法还将对建立.tap文件提供帮助:

We can also supply easy configuration for common cases with a makeService method that will also help build .tap files later:
{{{
#!python
# Easy configuration
# makeService from finger module

def makeService(config):
    # finger on port 79
    s = service.MultiService()
    f = FingerService(config['file'])
    h = internet.TCPServer(79, IFingerFactory(f))
    h.setServiceParent(s)

    # website on port 8000
    r = resource.IResource(f)
    r.templateDirectory = config['templates']
    site = server.Site(r)
    j = internet.TCPServer(8000, site)
    j.setServiceParent(s)

    # ssl on port 443
    if config.get('ssl'):
        k = internet.SSLServer(443, site, ServerContextFactory())
        k.setServiceParent(s)

    # irc fingerbot
    if config.has_key('ircnick'):
        i = IIRCClientFactory(f)
        i.nickname = config['ircnick']
        ircserver = config['ircserver']
        b = internet.TCPClient(ircserver, 6667, i)
        b.setServiceParent(s)

    # Pespective Broker on port 8889
    if config.has_key('pbport'):
        m = internet.TCPServer(
            int(config['pbport']),
            pb.PBServerFactory(IPerspectiveFinger(f)))
        m.setServiceParent(s)

    return s
}}}
Source listing - listings/finger/finger_config.py
同时我们现在可以编写更简单的文件:

And we can write simpler files now:
{{{
#!python
# simple-finger.tac
# eg:  twistd -ny simple-finger.tac

from twisted.application import service

import finger

options = { 'file': '/etc/users',
            'templates': '/usr/share/finger/templates',
            'ircnick': 'fingerbot',
            'ircserver': 'irc.freenode.net',
            'pbport': 8889,
            'ssl': 'ssl=0' }

ser = finger.makeService(options)
application = service.Application('finger', uid=1, gid=1)
ser.setServiceParent(service.IServiceCollection(application))
}}}
Source listing - listings/finger/simple-finger.tac
% twisted -ny simple-finger.tac

注意:这个finger用户仍然拥有基本的能力:他可以使用makeService,或者如果有特殊需要(也许是在另一个端口上的IRC?也许我们想让non-SSL的web服务器只在本地进行监听?等等,等等)的话,他可以使用底层接口。这是一个很重要的设计原则:永远不要强加抽象层:允许抽象层的使用。

Note: the finger user still has ultimate power: he can use makeService, or he can use the lower-level interface if he has specific needs (maybe an IRC server on some other port? maybe we want the non-SSL webserver to listen only locally? etc. etc.) This is an important design principle: never force a layer of abstraction: allow usage of layers of abstractions.
=== 意大利面条设计理论:(The pasta theory of design:) ===

细面条(Spaghetti):每一块要同其它代码块交互的代码[可以用GOTO、函数、对象来实现]
宽面条(Lasagna):拥有仔细设计过层的代码。每一个层是指在理论上独立。然后低级的层通常不易使用,而高级的层要依赖低级的层。
面卷(Ravioli):每一部分代码对自身都是有用的。在各部分之间有一个细的接口层[调料]。每一部分都可以用于其它地方。
...但有时候,用户只想要订面卷,所以在它之上有一个容易定义的谷粒(coarse-grain)抽象层更加有用。

Spaghetti: each piece of code interacts with every other piece of code [can be implemented with GOTO, functions, objects]
Lasagna: code has carefully designed layers. Each layer is, in theory independent. However low-level layers usually cannot be used easily, and high-level layers depend on low-level layers.
Ravioli: each part of the code is useful by itself. There is a thin layer of interfaces between various parts [the sauce]. Each part can be usefully be used elsewhere.
...but sometimes, the user just wants to order Ravioli, so one coarse-grain easily definable layer of abstraction on top of it all can be useful.

return index-->[[TwistedTUT]]

Version: 1.3.0