Trac组件架构

来源:互联网 发布:安卓看片软件哪个好 编辑:程序博客网 时间:2024/05/29 13:12

注:翻译得不是很好,欢迎指正

原文: http://trac.edgewall.org/wiki/TracDev/ComponentArchitecture

Trac Component Architecture¶

Trac组件架构

As the heart of Trac, trac.core implements a minimal 
component kernel that allows components to easily extend each others' 
functionality. It provides a "meta-plugin-API": every component can 
easily offer its own plugin API by declaring "extension points".

trac.core作为trac的核心,实现了一个小型的组件内核,这个内核允许组件很容易扩展其他组件的功能。

它提供了一个"meta-plugin-API": 每一个组件可以很方便通过声明"扩展点"来提供它的插件API。


What is a Component?¶

什么是组件

For our purposes, a component is an object that provides a certain 
type of service within the context of the application. There is at most one 
instance of any component: components are singletons. That implies that a 
component does not map to an entity of the application's object 
model; instead, components represent functional subsystems.

我们的目的是,一个组件是一个在应用程序的上下文中提供某种服务的对象。任何一个组件最多只有一个实例:
组件都是单件。这就意味着,一个组件不能映射成应用程序的对象模型;因此,组件应表现为功能子系统。

Components can declare "extension points" that other components can 
“plug in” to. This allows one component to enhance the functionality of the 
component it extends, without the extended component even knowing that the 
extending component exists. All that is needed is that the original component 
exposes – and uses – one or more extension points.

组件可以声明一些"扩展点",这些"扩展点"能被其他组件"plugin in" . 这就允许一个组件能够在没有被扩展组件、甚至不知道被扩展组件是否存在的情况下 增强该组件的功能。这一切仅需要 原始组件暴露-和使用-一个或多个扩展点。

Extension point graph


A component can extend any number of other components and still offer its own 
extension points. This allows a plugin to itself offer a plugin API (i.e. 
extension point). This feature is the basis for a plugin-based architecture.

一个组件可以扩展任意数目的其他组件,而且仍然可以提供它自己的扩展点。 这允许一个插件本身提供了一个插件的API (即扩展点) 。这是一个插件架构的基本特性

The actual functionality and APIs are defined by individual components. The 
component kernel provides the “magic glue” to hook the different subsystems 
together – without them necessarily knowing about each other.

组件定义实际功能和API。组件内核提供了"magic glue"来把不同的子系统组织在一起--不需要这些子系统一定要互相知道。

Public classes¶

公共类

trac.core.ComponentManager 

Manages component life cycle, instantiating registered components on demand 

         管理组件生命周期,处理组件注册实例的请求。

trac.core.Component 

Abstract base class for components.

         组件的抽象基类

trac.core.ExtensionPoint 

Declares an extension point on a component that other components can plug in 
to.

          在组件里声明一个扩展点,以便其他组件能够plugin in。

trac.core.Interface 

Every extension point specifies the contract that extenders must conform to 
via an Interface subclass.

         每个扩展点规定了契约,扩展者必须是一个Interface子类

Package diagram


Declaring a component¶

声明一个组件

The simplest possible component is an empty class derived from 
trac.core.Component:

一个最简单、可用的组件是继承于trac.core.Component 的空类:
from trac.core import *

class MyComponent(Component):
   pass

In the context of a component manager, this component can already be used:

在一个组件管理器的上下文里,组件可以这样使用:
    comp_mgr = ComponentManager()
   my_comp = MyComponent(comp_mgr)

Remember that components follow the singleton pattern. There is only one 
active instance of any component per component manager. The component 
constructor is “magic” in that it checks with the component manager whether 
there´s already an active instance before allocating a new instance. If the 
component was already instantiated, the existing instance is returned:

请记住,组件符合单件模式。在一个组件管理器里,任何一个组件只有一个可用的实例。组件的构造很''神秘",组件管理器会在分配一个新的实例前检查是否已有一个可用的实例。如果该组件已被实例化,这个已存在的实例会被返回。
    my_comp1 = MyComponent(comp_mgr)
   my_comp2 = MyComponent(comp_mgr)
   assert id(my_comp1) == id(my_comp2)

If a component needs to initialize data members, it can override the 
__init__ method. But because the component manager needs to be able to 
instantiate the component on demand, __init__ must not require 
any extra parameters, including the reference to the component manager 
passed into the constructor:

如果一个组件需要初始化数据成员,可以重写__init__方法。当因为组件管理器需要可以实例化组件的需求,__init__必须除了包含组件管理器的引用传递给构造函数以外,不能需要任何额外的参数。
    from trac.core import *

   class MyComponent(Component):
       def __init__(self):
           self.data = { }

   comp_mgr = ComponentManager()
   my_comp = MyComponent(comp_mgr)

Direct Component sub-classes also do not need to worry about 
invoking the base classes __init__ method (which is empty).

组件子类也不需要担心调用基类的__init__方法(这个方法为空的).

Declaring an extension point¶

声明一个扩展点

The component above doesn't actually do anything. Making an object a 
component only makes it act as a singleton in the scope of a component manager, 
which isn't that exciting in itself.

上面的那个组件实际上没做任何操作。在一个组件管理器的作用域里,生成一个组件的实例只需要

The real value of components becomes clearer when the facilities for 
extensions are used. As a simple example, the following component provides an 
extension point that lets other components listen to changes to the data it 
manages (in this case a list of to-do items) – following the widely known 
observable pattern:

当扩展使用了这些设施,组件的真正值会被清除。 一个简单的例子,下面这个组件提供了一个扩展点,这个扩展点允许其他组件能监听它管理的数据的改变(一组to-do的条目)--广为人知的观察者模式。
    from trac.core import *
   
   class ITodoObserver(Interface):
       def todo_added(name, description):
           """Called when a to-do item is added."""

   class TodoList(Component):
       observers = ExtensionPoint(ITodoObserver)

       def __init__(self):
           self.todos = { }

       def add(self, name, description):
           assert not name in self.todos, 'To-do already in list'
           self.todos[name] = description
           for observer in self.observers:
               observer.todo_added(name, description)

Here, the TodoList class declares an extension point called 
observers with the interface ITodoObserver. The interface 
defines the contract that extending components need to conform to.

这里,类TodoList 声明了一个扩展点,这个扩展点是一个被称作observers的接口ITodoObServer.
这个接口定义了契约,其他扩展组件需要遵守这个契约。

The TodoList component notifies the observers inside the 
add() method by iterating over self.observers and calling the 
todo_added() method for each. This works because the observers 
attribute is a descriptor: When it is accessed, it finds all registered 
components that declare to extend the extension point. For each of those 
components, it gets the instance from the component manager, potentially 
activating it if it is getting accessed for the first time.

TodoList组件在add()方法里遍历self.observers通知所有obserfers, 每个observers调用todo_add()方法.
这个能工作是因为observers属性(attribute )是一个descriptor:  当它被访问,它会找到所有已注册组件,并且这些组件要声明了扩展这个扩展点。它会从组件管理器里获取这些组件的实例,如果组件是第一次被访问有可能被激活。

Plugging in to an extension point¶

扩展一个扩展点

Now that we have an extendable component, let's add another component that 
extends it:

现在我们有了一个可被扩展的组件,让我们添加另一个组件来扩展它:
    class TodoPrinter(Component):
       implements(ITodoObserver)

       def todo_added(self, name, description):
           print 'TODO:', name
           print '     ', description

This class implements the ITodoObserver interface declared 
above, and simply prints every new to-do item to the console. By declaring to 
implement the interface, it transparently registers itself as an extension of 
the TodoList class.

这个类实现了上面声明的ITodoObserver接口,简单地把每个新的to-do 条目输出到控制台。
它通过声明实现这个接口,显示地注册它自己成为一个类TodoList 的扩展。

Note that you don't actually derive the component from 
the interface it implements. That is because conformance to an interface is 
orthogonal to inheritance; and because Python doesn't have static typing, 
there's no need to explicitly mark the component as implementing an 
interface.

          注意,实际上你不能把组件继承与它要实现的接口。这是因为接口的一致性与继承是正交的;
          也因为python没有静态数据类型,这明确表明组件没必要继承于接口。

You can specify multiple extension point interfaces to extend with the 
implements method by simply passing them as additional arguments.

你 定义多个扩展点接口来扩展implements 方法

Putting it together¶

把它们放在一起

Now that we've declared both a component exposing an extension point, and 
another component extending that extension point, let's use the to-do list 
example to see what happens:

既然我们已经声明了一个对外暴露了扩展点的组件,也声明了另一个扩展这个扩展点的组件,现在让我们
使用这个 to-do list 例子,看看会发生什么:
    comp_mgr = ComponentManager()
   todo_list = TodoList(comp_mgr)

   todo_list.add('Make coffee',
                 'Really need to make some coffee')
   todo_list.add('Bug triage',
                 'Double-check that all known issues were addressed')

Running this script will produce the following output:

运行这个脚本,会生成以下输出:

    TODO: Make coffee
         Really need to make some coffee
   TODO: Bug triage
         Double-check that all known issues were addressed

This output obviously comes from the TodoPrinter. Note however that 
the code snippet above doesn't even mention that class. All that is needed to 
have it participating in the action is to declare the class
(That implies 
that an extending class needs to be imported by a python script to be 
registered. The aspect of loading components is however separate from the 
extension mechanism itself
.)

这个输出结果显然来自于TodoPrinter。注意虽然上面的代码片段甚至没有提到这个类。而需要声明这个类,它能参与这个动作。( 这意味着一个扩展类需要通过导入python脚本来注册。把扩展机制和加载组件分离)

How Trac Uses Components¶

Trac是如何使用组件

The typical use of components in Trac starts with a top-level “service 
provider” that we'll pick up and use directly. Take, for example, trac.perm.PermissionSystem:

在Trac的组件的通常用法,一开始启动一个最高级别的"服务提供者",我们可以直接获得和使用。

trac.perm.PermissionSystem作为例子:

permission_system = trac.perm.PermissionSystem(env)
actions = permission_system.get_permission_actions()

Note that trac.env.Environment inherits 
trac.core.ComponentManager, so you'll typically see components 
initialized with an environment.

注意trac.env.Environment 是继承于trac.core.ComponentManager,所以你可以通常可以看到

          组件用environment来初始化

These are the first few lines of PermissionSystem as of r5790 (or in context):

这里有r5790版本的PermissionSystem 的前几行:
class PermissionSystem(Component):
   """Sub-system that manages user permissions."""

   implements(IPermissionRequestor)

   requestors = ExtensionPoint(IPermissionRequestor)

Note that this Component:

注意这个组件:
  1. implements the IPermissionRequestor interface . 实现了IPermissionRequestor 接口。
  2. has an extension point for registering all the Components implementing 
    IPermissionRequestor (in context): 有一个扩展点,所有注册的组件都可以实现IPermissionRequestor 。
    class IPermissionRequestor(Interface):
       """Extension point interface for components that define actions."""

       def get_permission_actions():
           """Return a list of actions defined by this component."""

Note that interface authors have not always been consistent about 
declaring the “self” parameter in signatures.

注意接口的定义者已经没有符合在签名里(注,方法)定义 "self"参数。


When we use PermissionSystem, the plugin system will have 
automatically gathered up all implementations of IPermissionRequestor 
and placed them in PermissionSystem's list of requestors. In 
this specific case PermissionSystem will be part of that list as well, 
because it implements the IPermissionRequestor interface. In no way a 
Component is bound to implement the interfaces it declares an extension point 
for, the two operations being entirely independent. But when that make sense, 
it's entirely possible to do so.

当我们使用PermissionSystem,插件系统会自动收集IPermissionRequestor的所有实现,然后把它们放入PermissionSystem的requestors列表里。 在这种特殊情况下,PermissionSystem 也会是这个列表的一员,
因为它实现了IPermissionRequestor 接口。一个组件一定不要实现它声明的扩展点,这两个操作完全是独立的。
但当这样做是有意义的,完全可以这样做的。

Note: it's certainly debatable whether it makes sense in this particular 
case—but if you do decide to do it, watch out for infinite 
recursion as PermissionStore does here. 

注意: 当然在这种情况下有意义的是有争议--但当你决定这样做,当心无穷递归,就像这里的

PermissionStore 

Next in PermissionSystem there is a declaration of an 
ExtensionOption called store:

接下来PermissionSystem 声明了一个ExtensionOption 的store:

    store = ExtensionOption('trac', 'permission_store', IPermissionStore,
                           'DefaultPermissionStore',
       """Name of the component implementing `IPermissionStore`, which is used
       for managing user and group permissions."""
)

The above adds an option called permission_store to 
trac.ini, declares that the component named by the option implements 
IPermissionStore, and sets its default to 
DefaultPermissionStore. See trac.config for 
ExtensionOption and friends. Methods of service providers such as 
PermissionSystem are commonly a thin forwarding layer over such an 
ExtensionOption. For example:

上面的代码在trac.ini里添加一个选项permission_store,声明了一个选项实现类IPermissionStore的组件,
设置这个组件为DefaultPermissionStore的默认值。请看trac.config的ExtensionOption和其他部分。
PermissionSystem 的服务提供者的方法一般是一个瘦的转发层,就像ExtensionOption。例如:
    def get_all_permissions(self):
       """Return all permissions for all users.

       The permissions are returned as a list of (subject, action)
       formatted tuples."""

       return self.store.get_all_permissions()

Thus, service providers are directly manipulated from Python, and are 
customized through the automatic aggregation of components implementing 
ExtensionPoints and through configuration of ExtensionOption
by Trac administrators.

因此,服务提供者是直接被python操纵,被ExtensionPoints组件的自动聚合定制,被Trac管理员的ExtensionOptions 配置定制。

原创粉丝点击