Finger演化:生成finger库

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

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

2. 组织(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.

   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.

3. 容易的配置(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:

   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:

   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.

3.1. 意大利面条设计理论:(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