设计 Twisted 应用程序

目标

本文档描述了如何构建一个良好的 Twisted 应用程序。它应该对想要以干净、可维护的方式构建代码的 Twisted 初学者很有用,这种方式反映了当前的最佳实践。

读者需要熟悉使用 Twisted 编写 服务器客户端

模块化设计的示例:TwistedQuotes

TwistedQuotes 是一个非常简单的插件,它很好地展示了 Twisted 的强大功能。它将导出一个小的功能内核——每日格言——可以通过 Twisted 支持的每个接口访问:网页、电子邮件、即时消息、特定的每日格言协议等等。

设置项目目录

请参阅 设置 TwistedQuotes 示例 的描述。

看一下应用程序的核心

quoters.py

from random import choice

from zope.interface import implementer

from TwistedQuotes import quoteproto


@implementer(quoteproto.IQuoter)
class StaticQuoter:
    """
    Return a static quote.
    """

    def __init__(self, quote):
        self.quote = quote

    def getQuote(self):
        return self.quote


@implementer(quoteproto.IQuoter)
class FortuneQuoter:
    """
    Load quotes from a fortune-format file.
    """

    def __init__(self, filenames):
        self.filenames = filenames

    def getQuote(self):
        with open(choice(self.filenames)) as quoteFile:
            quotes = quoteFile.read().split("\n%\n")
        return choice(quotes)

此代码清单向我们展示了 Twisted Quotes 系统的本质。代码没有与外部世界交互的方式,但它提供了一个库,这是一个清晰且简洁的抽象:“给我今天的格言”。

请注意,此模块根本没有导入任何 Twisted 功能!这样做是为了集成。如果你的“业务对象”没有绑定到你的用户界面,你可以创建一个模块,该模块可以将这些对象与不同的协议、GUI 和文件格式集成。拥有这样的类提供了一种方法,通过允许每个组件独立使用,来解耦你的组件。

通过这种方式,Twisted 本身对你的程序逻辑的影响最小。尽管 Twisted 的“点产品”具有高度互操作性,但它们也遵循这种方法。你可以独立使用它们,因为它们没有绑定在一起。它们以明确定义的方式进行通信,并且只有当这种通信提供一些附加功能时才会进行通信。因此,你可以将 twisted.webtwisted.enterprise 一起使用,但两者都不需要对方,因为它们是围绕 Deferred 的概念集成的。

你的 Twisted 应用程序应该尽可能地遵循这种风格。至少要有一个模块实现你的特定功能,独立于任何用户界面代码。

接下来,我们需要将这种抽象逻辑与某种显示给用户的方式相关联。我们将通过编写一个 Twisted 服务器协议来做到这一点,该协议将通过向连接到它的客户端发送一个格言并关闭连接来响应客户端。注意:不要过于关注此处的细节——与用户交互的不同方式是 Twisted 做的 90%,并且有很多文档描述了不同的方法。

quoteproto.py

from zope.interface import Interface

from twisted.internet.protocol import Factory, Protocol


class IQuoter(Interface):
    """
    An object that returns quotes.
    """

    def getQuote():
        """
        Return a quote.
        """


class QOTD(Protocol):
    def connectionMade(self):
        self.transport.write(self.factory.quoter.getQuote() + "\r\n")
        self.transport.loseConnection()


class QOTDFactory(Factory):
    """
    A factory for the Quote of the Day protocol.

    @type quoter: L{IQuoter} provider
    @ivar quoter: An object which provides L{IQuoter} which will be used by
        the L{QOTD} protocol to get quotes to emit.
    """

    protocol = QOTD

    def __init__(self, quoter):
        self.quoter = quoter

这是一个非常简单的 Protocol 实现,上面描述的模式在这里重复出现。该协议本质上不包含任何自己的逻辑,只是足以将一个可以生成格言的对象(一个 Quoter)和一个可以将字节中继到 TCP 连接的对象(一个 Transport)绑定在一起。当客户端连接到此服务器时,将创建一个 QOTD 实例,并调用其 connectionMade 方法。

QOTDFactory 的作用是向 Twisted 框架指定如何创建一个 Protocol 实例来处理连接。Twisted 不会实例化 QOTDFactory;你将在稍后在 twistd 插件中自己完成此操作。

注意:你可以在 编写服务器 HOWTO 中阅读有关 ProtocolFactory 的更多详细信息。

一旦我们有了抽象——一个 Quoter——并且我们有了将它连接到网络的机制——QOTD 协议——接下来要做的是在抽象和用户之间建立功能链的最后一个环节。这个最后一个环节将允许用户选择一个 Quoter 并配置协议。编写此配置在 应用程序 HOWTO 中介绍。