Differences between revisions 1 and 2
Revision 1 as of 2004-08-09 02:16:43
Size: 7747
Editor: dreamingk
Comment:
Revision 2 as of 2009-12-25 07:19:08
Size: 7747
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
-- dreamingk [[[DateTime(2004-08-09T02:16:43Z)]]]
[[TableOfContents]]
-- dreamingk [<<DateTime(2004-08-09T02:16:43Z)>>]
<<TableOfContents>>
Line 159: Line 159:
return index-->["TwistedTUT"] return index-->[[TwistedTUT]]

Finger演化:生成finger库

The Evolution of Finger: making a finger library -- dreamingk [2004-08-09 02:16:43]

介绍(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.

Toggle line numbers
   1 # organized-finger.tac
   2 # eg:  twistd -ny organized-finger.tac
   3 
   4 import finger
   5 
   6 from twisted.internet import protocol, reactor, defer
   7 from twisted.spread import pb
   8 from twisted.web import resource, server
   9 from twisted.application import internet, service, strports
  10 from twisted.python import log
  11 
  12 application = service.Application('finger', uid=1, gid=1)
  13 f = finger.FingerService('/etc/users')
  14 serviceCollection = service.IServiceCollection(application)
  15 internet.TCPServer(79, finger.IFingerFactory(f)
  16                    ).setServiceParent(serviceCollection)
  17 
  18 site = server.Site(resource.IResource(f))
  19 internet.TCPServer(8000, site
  20                    ).setServiceParent(serviceCollection)
  21 
  22 internet.SSLServer(443, site, ServerContextFactory()
  23                    ).setServiceParent(serviceCollection)
  24 
  25 i = finger.IIRCClientFactory(f)
  26 i.nickname = 'fingerbot'
  27 internet.TCPClient('irc.freenode.org', 6667, i
  28                    ).setServiceParent(serviceCollection)
  29 
  30 internet.TCPServer(8889, pb.PBServerFactory(finger.IPerspectiveFinger(f))
  31                    ).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:

Toggle line numbers
   1 # Easy configuration
   2 # makeService from finger module
   3 
   4 def makeService(config):
   5     # finger on port 79
   6     s = service.MultiService()
   7     f = FingerService(config['file'])
   8     h = internet.TCPServer(79, IFingerFactory(f))
   9     h.setServiceParent(s)
  10 
  11     # website on port 8000
  12     r = resource.IResource(f)
  13     r.templateDirectory = config['templates']
  14     site = server.Site(r)
  15     j = internet.TCPServer(8000, site)
  16     j.setServiceParent(s)
  17 
  18     # ssl on port 443
  19     if config.get('ssl'):
  20         k = internet.SSLServer(443, site, ServerContextFactory())
  21         k.setServiceParent(s)
  22 
  23     # irc fingerbot
  24     if config.has_key('ircnick'):
  25         i = IIRCClientFactory(f)
  26         i.nickname = config['ircnick']
  27         ircserver = config['ircserver']
  28         b = internet.TCPClient(ircserver, 6667, i)
  29         b.setServiceParent(s)
  30 
  31     # Pespective Broker on port 8889
  32     if config.has_key('pbport'):
  33         m = internet.TCPServer(
  34             int(config['pbport']),
  35             pb.PBServerFactory(IPerspectiveFinger(f)))
  36         m.setServiceParent(s)
  37 
  38     return s

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

And we can write simpler files now:

Toggle line numbers
   1 # simple-finger.tac
   2 # eg:  twistd -ny simple-finger.tac
   3 
   4 from twisted.application import service
   5 
   6 import finger
   7 
   8 options = { 'file': '/etc/users',
   9             'templates': '/usr/share/finger/templates',
  10             'ircnick': 'fingerbot',
  11             'ircserver': 'irc.freenode.net',
  12             'pbport': 8889,
  13             'ssl': 'ssl=0' }
  14 
  15 ser = finger.makeService(options)
  16 application = service.Application('finger', uid=1, gid=1)
  17 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

twistedTUT10 (last edited 2009-12-25 07:19:08 by localhost)