##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