Five体系介绍

Five体系介绍
Five是为了将最新的Zope3技术应用于Zope2中而引入的。 two + three = five,这是Five名称的由来。
本文将重点介绍Five体系,及其在Zope2中的应用。
Five 体系介绍(zope3 子集)
Five让Zope 2 开发者用Zope 3技术应用于Zope 2中。
Five can be used inside your current Zope 2 project. The benefits are:
Five有如下优点:
  • availability of Zope 3 technologies in Zope 2 like the component architecture and declarative configuration.
  • 使得Zope 3 技术在Zope 2中有效。
  • you can gradually evolve your Zope 2 project so it is better positioned for the migration to Zope 3.
  • 方便升级Zope 2 产品,以便将来迁移到Zope 3 。
  • you start learning about Zope 3 right now, preparing yourself better for the future. Since Zope 3 is open to contributions, you could even influence your future for the better.
  • 马上通过five学习Zope 3 技术。
Five can also be used to develop new Zope 2 products, though depending on your deployment requirements it might in that case make more sense to develop for Zope 3 directly.
Five 也能用来开发Zope 2产品。
Five is only useful on the Python (Product) level in Zope 2, not from within the Zope Management Interface. Five makes no attempt to provide a user interface, but is aimed squarely at the Python developer.
Five 仅仅在Zope 2的python级别有用,而不提供任何ZMI接口。

Zope 3 interfaces
接口是描述对象的一个窗口。例如,一个对象对外提供的属性和方法就是接口所要描述的东西。
从源码分析看,接口很象一个类的定义,只不过没有对属性和方法的实现代码。
from zope.interface import Interface # by convention, all interfaces are prefixed with ``I`` class IElephant(Interface): """An elephant is a big object that barely fits in the cupboard. """ def getAngerLevel(): """Anger level, maximum of 100. The longer the elephant has been in the cupboard, the angrier. """ def isInCupboard(): """Returns true if the elephant is indeed in cupboard. """ def trunkSmash(target): """Smash the target with trunk. The anger level determines the force of the hit. """ def trample(target): """Trample the target. The anger level determines the rate of flattening of the target. """接口定义完后,一个具体的类定义可以来认领该接口,(当某个类实现了该接口后,该类所有实例都提供该接口描述的属性和方法。)
class PinkElephant: # this says all instances of this class provide IElephant implements(IElephant) def getAngerLevel(self): return 0 # this elephant is peaceful def isInCupboard(self): return False # it's never in a cupboard but can be found in bottles def trunkSmash(self, target): target.tickle() def trample(self, target): target.patOnHead()为什么要定义接口,原因在于他有如下好处:
  • 接口容易提供好理解的API文档
  • 帮助开发人员实现可视化设计(当然这个方面还在加强中)
  • 如果一个对象提供了一个接口,那么该对象可以在ZOPE3结构中作为一个部件。
为了使用FIVE,你将必须使得你的对象提供接口。有时,你无法更改类定义的源代码(由于不是代码维护人员),但你仍然想实现某个接口。FIVE提供一种ZCML语句来做到这个。
<five:implements class="tolkien.Oliphant" implements="interfaces.IElephant" />ZOPE2和ZOPE3中接口的对比
你或许非常熟悉ZOPE2中申明接口的方法。ZOPE2中用__implements__ 的类属性来做接口申明。ZOPE2不能发现ZOPE3接口,ZOPE3机制也不能发现ZOPE2接口。这其实是件好事情。由于ZOPE2没法和ZOPE3接口交互,ZOPE3也不能理解ZOPE2接口,这样可以安全地定义一个类,使得ZOPE2和ZOPE3接口完全独立。当然这种需要是非常罕见的,通常情况下,你最好是关断__implements__ 方式,而用implements() 方式来让应用采用FIVE体系。
Switching from Zope 2 interfaces to Zope 3 interfaces is easy – just make your interfaces inherit from zope.interface.Interface instead of Interface.Interface (or Interface.Base). Next, change all __implements__ to implements().
从Zope 2 接口切换到Zope 3接口非常简单,只需在接口定义中用继承zope.interface.Interface 代替继承Interface.Interface (或者Interface.Base),并将所有 __implements__用implements().代替即可。

这将让你的应用仍然能够很好工作(FIVE体系中)。不过,你还应当在调用中将isImplementedBy更改为providedBy,因为,isImplementedBy模式将逐渐取消使用。


适配器
从一个python程序员角度来看,FIVE带来的最直接的信息是 适配器。本段将通过实例代码来解释FIVE体系中每一个部件怎样有机结合在一起。

Zope 3 适配器必须依靠Zope 3 接口。下面是创建Zope 3 接口的代码:

from zope.interface import Interface class IMyInterface(Interface): """This is a Zope 3 interface. """ def someMethod(): """This method does amazing stuff. """现在定义一个类来认领(实现)该接口,注意需要用到implements() ,代码如下:
from zope.interface import implements from interfaces import IMyInterface class MyClass: implements(IMyInterface) def someMethod(self): return "I am alive! Alive!"现在,为了引入适配器,我们来定义一个新的接口,该接口将用在适配器例子中:
class INewInterface(Interface): """The interface we adapt to. """ def anotherMethod(): """This method does more stuff. """接下来,我们定义一个类来实现这个适配器。用类来实现适配器相当简单,仅仅需要一个context对象来作constructor(构造器)。这个context对象将是被适配的对象。代码如下:
from zope.interface import implements from interfaces import INewInterface class MyAdapter: implements(INewInterface) def __init__(self, context): self.context = context def anotherMethod(self): return "We have adapted: %s" % self.context.someMethod()跟着,我们通过ZCML将所有东西挂在一起。所有类定义在classes.py模块中,所有接口都定义在interfaces.py模块中,我们申明MyAdapter作为一个从IMyInterface到INewInterface的适配器(通过configure.zcml来完成)
<configure xmlns="http://namespaces.zope.org/zope"> <adapter for=".interfaces.IMyInterface" provides=".interfaces.INewInterface" factory=".classes.MyAdapter" /> </configure>FIVE将自动提取产品目录下的configure.zcml 文件,现在任何提供IMyInterface接口的对象能通过适配器,提供INewInterface接口。
from classes import MyClass from interfaces import INewInterface object = MyClass() adapted = INewInterface(object) print adapted.anotherMethod()
FIVE体系中的视图
这段将介绍FIVE体现的视图系统。demo/FiveViewsDemo 是个演示产品,配合说明视图,另外tests/products/FiveTest 也包含了一些测试视图,还可以参考下ZOPE 3的视图体系,事实上FIVE是ZOPE 3的子功能集,能和ZOPE 2协同工作。
FIVE允许为对象创建视图,即便是内建对象,只要符合下面两点:

  • 对象提供Zope 3接口
  • The object (typically its class) is made Zope 3 traversable. This allows Zope 3 views, resources and other things to be attached to a Zope 2 object.
  • 对象为Zope 3可穿过的。这将允许Zope 3视图、资源和其他东西可以附加到Zope 2对象。
典型你可以给你的类一个接口,代码如下:
class MyClass: implements(ISomeInterface)
不过对于已经存在的对象修改类定义是不可能的。这时借助ZCML来完成。作为例子,如果我们要建一个ZOPE folder(文件夹)来实现 IFolder接口,可以在ZCML编写:
<five:implements class="OFS.Folder.Folder" interface=".interfaces.IFolder" />five in this case refers to the XML namespace for Five, http://namespace.zope.org/five.
FIVE在这种情况下将引用XML命名空间,http://namespace.zope.org/five.。

Views in Five are simple classes. The only requirements for a Five view class are:
视图在FIVE体系中是很简单的类,仅仅要求符合如下条件:
  • They need an __init__() that take a context and a request attribute. Typically this comes from a base class, such as BrowserView.
  • 需要一个__init__()来取得上下文和request属性,通常这个来自于基类,如BrowserView
  • They need to be initialized with the Zope 2 security system, as otherwise you cannot use the view.
  • 需要和Zope 2的安全体现一起初始化,否则,不能使用该视图。
  • This also means they need to be part of the Zope 2 acquisition system, as this is a requirement for Zope 2 security to function. The BrowserView base class, available from Products.Five, already inherits from Acquisition.Explicit to make this be the case. Acquisition is explicit so no attributes can be acquired by accident.
  • 他们也需要是Zope 2 acquisition 体系的一部分,这时Zope 2安全体系要求的。BrowserView基类已经继承了。
An example of a simple view:
样例视图:
from Products.Five import BrowserView class SimpleFolderView(BrowserView): security = ClassSecurityInfo() security.declarePublic('eagle') def eagle(self): """Test """ return "The eagle has landed: %s" % self.context.objectIds() InitializeClass(SimpleFolderView)Note that it is not a good idea to give a view class its own index_html, as this confuses Five's view lookup machinery.
注意,不要给一个视图类一个自己的index_html,这将混淆five视图查找机制。
As you can see, the class is initialized with the Zope 2 security system. This view uses methods in Python, but you can also use other Zope 2 mechanisms such as PageTemplateFile.
正如你所见,视图类初始化用ZOPE2 安全体系。例子中视图用python方法,同样你也可以用ZOPE2 的其他机制,比如ZPT.
Finally, we need to hook up the pages through ZCML:
最后,我们用ZCML将视图挂接起来:
<browser:page for=".interfaces.IFolder" class=".browser.SimpleFolderView" attribute="eagle" name="eagle.txt" permission="zope2.ViewManagementScreens" />这里浏览器将引用ZOPE3的XML名称空间http://namespace.zope.org/browser。申明ZOPE 2权限。 permissions.zcml 文件包含了从ZOPE 2权限到ZOPE 3名称的一个映射。

设置