Zope3宝典/接口介绍

来自Ubuntu中文
跳到导航跳到搜索

Chapter 6:An Introduction to Interfaces(第 6 章:接口简介)


原文出处:The Zope 3 Developers Book - An Introduction for Python Programmers

原文作者:StephanRichter, zope.org

授权许可:创作共用协议

翻译人员:

校对人员:Leal, FireHare

适用版本:Zope 3

文章状态:校正阶段



Difficulty(难度)

Newcomer(新手)

Skills(技能)

  • You should know Python well, which is a requirement for all chapters.
    你应该相当熟悉Python,所有章节都要求这一点。
  • Some knowledge about the usage of formal interfaces in software design. Optional.
    了解软件设计里规范接口的使用。(非必需)

Problem/Task(问题/任务)

In every chapter in this book you will hear about interfaces in one way or another. Hence it is very important for the reader to understand the purpose of interfaces.
本书所有章节里,你都无法避开接口这一概念,不论以何种形式。因此,对读者而言,理解接口的意义相当重要。

Solution(解决方案)

6.1 Introduction

In very large software projects, especially where the interaction with a lot of other software is expected and desired, it is necessary to develop well-specified application programming interfaces (APIs). We could think of APIs as standards of the framework, such as the RFC or POSIX standards. Once an interface is defined and made public, it should be very hard to change it. But an API is also useful inside a single software, known as internal API.
在大型软件项目中,特别是那些预期或需要与大量其它软件互动的项目中,开发精确良好的应用开发接口(APIs)是非常有必要的。我们可以把API看作整个框架的标准,就像RFC或POSIX标准那样。一旦定义并发布了一个接口,它就不应随意改变。此外,API在单体软件里也很有用,它被看作内部API。

Interfaces (in the sense we will use the term) provide a programmatic way to specify the API in a programming language.
接口(感觉我们要使用术语)在编程中提供一个用来指定 API 的大概方法。

While other modern programming languages like Java use interfaces as a native language feature, Python did not even have the equivalent of a formal interface until recently. Usually, in Python the API is defined by the class that implements it, and it was up to the documentation to create a formal representation of the API. This approach has many issues. Often developers changed the API of a class without realizing that they broke many other people’s code. Programmed interfaces can completely resolve this issue, since alarm bells can be rung (via tests) as soon as an API breakage occurs. Here is a simple example of a Python interface (as used by the Zope project):
当其他编程语言,例如Java,就将接口作为语言内部的特性,而直到最近Python才有了同等意义的正式的接口。通常,在Python里接口是通过类(class)来定义并实现的,并且它是通过文档来创建的API的表现。有很多关于这方面的文章。开发人员经常在没有意识到破坏了其他人的代码的情况下修改了类的API。编程接口可以完全解决这个问题,当一个API被破坏时警铃就会响起(通过测试)。下面是一个简单的Python接口的例子(已经被使用在Zope项目中):

#!python
from zope.interface import Interface, Attribute

class IExample(Interface):
"""This interface represents a generic example."""

text = Attribute("The text of the example")

def setText(text):
"This method writes the passed text to the text attribute."

def getText():
"This method returns the value of the text attribute."
  • Line 1: We import the only two important objects from zope.interface here, the meta-class Interface and the class Attribute.
    行 1:我们从zope.interface导入了2个重要的对象--元类Interface,类Attribute。
  • Line 3: We “misuse” the class declaration to declare interfaces as well. Note though, that interfaces are not classes and do not behave as such! Using Interface as base class will make this object an interface.
    行 3:我们"误用"定义类的方式来定义接口。注意,虽然接口不是类并且也没有类的行为,但是当你把接口作为类的基类时,这个对象就变成了接口。
  • Line 4: In Zope 3 interfaces are the main source for API documentation, so that it is necessary to always write very descriptive doc strings. The interface doc string gives a detailed explanation on how objects implementing this interface are expected to function.
    行 4:在Zope3里,这是接口的文档。所以,在这里写上明确的文档字符串是很必要的。接口文档字符串给出了实现接口功能的详细描述。
  • Line 6: The Attribute class is used to declare attributes in an interface. The constructor of this class only takes one string argument, which is the documentation of this attribute. You might say now that there is much more meta-data that an attribute could have, such as the minimum value of an integer or the maximum length of a string for example. This type of extensions to the Attribute class are provided by another module called zope.schema, which is described in “Zope Schemas and Widgets”.
    行 6:Attribute类是用来在接口中声明属性的。这个类的构造方法仅接受一个字符串参数,就是这个属性的文档字符串。现在你可能会说:"还有很多的元数据属性存在,例如整型的最小值或者字符串的最大长度等。"这一类型的扩展属性类是由另外一个模块提供的,叫zope.schema。我们将在"ZOPE的模型和表单部件"章节中阐述。
  • Line 8-9 & 11-12: Methods are declared using the def keyword as usual. The difference is, though, that the first argument is not self. You only list all of the common arguments. The doc string is again used for documentation.
    行 8-9和11-12:像通常那样使用def关键字来定义方法。不同的是第一个参数不是self。你只是列出了所有的公共参数。文档字符串依然作为文档来使用的。

Other than that, methods can contain anything you like; yet, Zope does not use anything else of the method content. If you use the zope. interface package apart from Zope 3, you could use the method body to provide formal descriptions of pre- and postconditions, argument types and return types.
除此之外,方法中可以包含任何你想要的东西,Zope不会使用方法里的任何内容。如果你在Zope3之外使用zope.interface包,那么你可以方法体来为先决条件、参数类型和返回类型提供一个正式的描述。

The above is a typical but not practical example of an interface. Since we use Python, it is not necessary to specify both, the attribute and the setter/getter methods. In this case we would usually just specify the attribute as part of the interface and implement it as a Python property if necessary.
上面是一个典型的但不实际的接口例子。既然我们使用Python,就没有必要同时为属性实现setter和getter方法。在这种情况下,我们通常可以将属性制定为接口的一部分并且用Python的属性来实现它,如果需要的话。

6.2 Advanced Uses(6.2 高级用法)

Once you have Python-based interfaces, many new possibilities develop. You can now use interfaces as objects that can define contracts. For example, you can say that there is a class AllText that converts IExample to IAllText, where the latter interface has a method getAllText() that returns all human-readable text from IExample. Such a class is known as an Adapter. More formally, adapters use one interface ( IExample) to provide/implement another interface ( IAllText).
一旦你有了基于Python的接口,很多新的可能性逐步显示出来了。现在你可以使用接口来作为定义对象的构造函数。例如,你可以说有一个类AllText可以将IExample转换为IAllText,后者的接口含有一个getAllText()的方法,可以从IExample返回所有人能读懂的文本。这样的类被称作适配器(Adapter)。更加正式的来讲,适配器就是使用一个接口(IExample)来提供/实现另外一个接口(IAllText)。

Even more commonly, interfaces are used for identification. Zope 3’s utility registry often executes queries of the form: “Give me all utilities that implement interface I1.” Interfaces are even used to classify other interfaces! For example, I might declare my IExample interface to be an IContentType. I can then go to the utility registry and ask: “Give me all interfaces that represent a content type ( IContentType).” Once I know these content type interfaces, I can figure out which classes are content types.
更加通用的来讲,接口是被用作标识符。Zope3的工具注册经常执行查询表单:"给我所有实现了接口I1的工具。"接口甚至被用来分类其它接口!例如,我可能我的IExample声明为IContentType,那我局可以到工具注册的地方查询:"给我所有表现为一种类型(IContentType)的接口。"一旦我知道了这些内容类型的接口,我就可以指出哪个类是内容类型。

You can see that interfaces provide a solution to a wide range of use cases. By the way, it is very common to create empty interfaces purely for identification purposes; these interfaces are known as “marker interfaces”.
你可以看到接口为广泛的用例提供了解决方案。顺便说一下,创建一个空的接口用来做为标识的用途是非常普遍的,这一类的接口被称作"标记接口"。

6.3 Using Interfaces(6.3 使用接口)

In objects interfaces are used as follows:
在对象里接口是这样被使用的:

#!python
1  from zope.interface import implements
2  from interfaces import IExample
3  
4  class SimpleExample:
5      implements(IExample)

The implements() method tells the system that instances of the class provide IExample. But of course, modules and classes themselves can implement interfaces as well. For modules you can use moduleProvides(*interfaces). For classes you can insert classImplements(*interfaces) directly in the class definition or use classProvides(cls,*interfaces) for an existing class. Also, you can use directlyProvides(instance,*interfaces) for any object as well (including instances of classes).
implements()方法告诉系统这个类的实例提供IExample。但是当然,模块和类本身也可以实现接口。对于模块来说你可以使用moduleProvides(*interfaces)。对于类来说,你可以直接在类的定义里插入classImplements(*interfaces)或者对现有的类使用classProvides(cls,*interfaces)。同样,你可以使用在任何对象(包括类的实例)上使用directlyProvides(instance,*interfaces)。

The Interface object itself has some nice methods. The most common one is providedBy(ob), which checks whether the passed object implements the interface or not:
接口对象本身含有一些非常棒的方法。最普通的一个就是providedBy(ob),它可以检测传递过来的对象是否是实现了这个接口

#!python
1  >>> ex = SimpleExample()
2  >>> IExample.providedBy(ex)
3  True

Similarly you can pass in a class and test whether instances of this class implement the interface by calling IExample.implementedBy(SimpleExample).同样的,你也可以通过调用IExample.implementedBy(SimpleExample)来传递到类里面并测试这个类的实例是否实现了这个接口。

The last useful method is isOrExtends(interface). This method checks whether the passed interface equals the interface or whether the passed interface is a base (extends) of the interface.
最后一个有用的方法是isOrExtends(interface)。这个方法用来检测传递的接口是否是接口或者传递的接口是否是这个接口的基(继承)接口。

When creating classes from an interface, there is a helpful script called pyskel.py that creates a class skeleton from an interface. Before using the script you have to make sure the ZOPE3/src is in your Python path. Usage:
当丛接口创建类的时候,有一个很有帮助性的脚本叫pyskel.p,它可以帮助你生成类的骨架。在使用这个脚本之前,请确保ZOPE3/src在你的Python路径中。用法:

1  python2.3 utilities/pyskel.py dotted.path.ref.to.interface

This call creates a skeleton of the class in your console, which saves you a lot of typing. The order of the attributes and methods as they appear in the interface is preserved.
这个调用将在你的控制台里创建类的骨架,它可以帮助你省去大量的输入。属性和方法的顺序与在接口里面出现的顺序是一样的。

As a last note, since Python does not comone with interfaces, the zope.interface package provides some interfaces that are implemented by the built-in Python types. They can be found in zope.interface.common.
最后需要注意的是,由于Python没有提供接口,那么zope. Interface包提供的一些实现了Python内建类型的接口。你可以在zope.interface.common里找到它们。

This concludes the overview of interfaces. This introduction should be sufficient to get you started with the chapters. Many of the concepts will become much clearer as you work through the hands-on examples of the following parts of the book.
这就是接口的概览。这些介绍应该足够你开始下面章节的学习了。更多的概念将在你学习和使用本书余下部分的过程中变得愈加清晰起来。