服务
我们知道一个单继承接口仅说明对象的一个方面。但是,对象通常会包含多个方面。UNO 使用多继承接口和服务来指定包含多个方面的完整对象。
在第一步中,对象的所有各个方面(通常用单继承接口表示)被组合到一个多继承接口类型中。如果通过调用特定的工厂方法可获得这样的对象,则此步骤即所需的全部操作。指定工厂方法以返回给定的多继承 接口类型的值。但是,如果这样的对象在全局组件上下文中可用作常规服务,则在第二步中必须有服务说明。此服务说明将为新样式,能够将服务名称(在组件上下文中通过它可获得服务)映射成给定的多继承接口类型。
为了实现向后兼容,还存在旧式服务,其中包括一组支持某一功能所需的单继承接口和属性。这样的服务还可以包括其他旧式服务。旧式服务的主要缺点在于,它无法明确指出其说明的是通过特定的工厂方法获得的对象(因此将不存在新式服务说明),还是说明在全局组件上下文中可以获得的常规服务(因此将存在新式服务说明)。
从 UNO 对象用户的角度来看,对象提供 API 参考书中所述的一项服务,有时甚至提供多项独立的、多继承接口或旧式的服务。可以通过接口中分组的方法调用以及也是经过特殊接口处理的属性来使用服务。由于对功能的访问仅由接口提供,因此,希望使用某个对象的用户无需关心如何实现。
从 UNO 对象实现者的角度来看,多继承接口和旧式服务用于定义某项与编程语言无关的功能,且无需提供有关对象内部实现的说明。实现某个对象意味着必须支持所有指定的接口和属性。一个 UNO 对象可以实现多项独立的、多继承接口或旧式服务。有时,实现两项或更多独立的、多继承接口或服务非常有用,因为它们具有相关的功能,或者支持对象的不同视图。
插图 接口、服务和实现 说明了接口和服务之间的关系。具有多个接口的旧式服务的与语言无关规范用于实现符合此规范的 UNO 对象。这样的 UNO 对象有时被称为“组件”,尽管该术语用于说明 UNO 环境内的配置实体更为准确。插图使用的是直接支持多个接口的旧式服务说明,对于新式服务说明,唯一的区别就是它仅支持一个多继承接口,该接口将继承其他接口。
插图 RemoteControl 服务 说明了接口和服务之间的关系。具有多个接口的旧式服务的与语言无关规范用于实现符合此规范的 UNO 对象。这样的 UNO 对象有时被称为“组件”,尽管该术语用于说明 UNO 环境内的配置实体更为准确。插图使用的是直接支持多个接口的旧式服务说明,对于新式服务说明,唯一的区别就是它仅支持一个多继承接口,该接口将继承其他接口。
可以按照服务规范来说明包含电视机和遥控的电视系统的功能。前面所述的 XPower
和 XChannel
接口是服务规范 RemoteControl
中的一部分。新服务 TVSet
包含三个接口:XPower
、XChannel
和 XStandby
,用于控制电源、频道选择、附加电源功能 standby()
以及 timer()
功能。
引用接口
对某项服务定义中接口的引用意味着要实现此服务必须提供指定的接口。此外,还可以提供可选接口。如果某个多继承接口继承可选接口,或者某个旧式服务包含可选接口,则任何给定的 UNO 对象可以支持此接口,也可以不支持。如果您使用某个 UNO 对象的可选接口,通常需要检查 queryInterface()
的结果是否为 null
,并作出相应的反应,否则,您的代码将与不包含可选接口的实现不兼容,并会因为出现空指针异常而结束。以下 UNOIDL 代码段说明了 OpenOffice.org API 中 com.sun.star.text.TextDocument 的旧式服务的规范片段。请注意方括号中的标志 optional,此标志表示接口 XFootnotesSupplier
和 XEndnotesSupplier
为可选接口。
// com.sun.star.text.TextDocument service TextDocument { ... interface com::sun::star::text::XTextDocument; interface com::sun::star::util::XSearchable; interface com::sun::star::util::XRefreshable; [optional] interface com::sun::star::text::XFootnotesSupplier; [optional] interface com::sun::star::text::XEndnotesSupplier; ... };
服务构造函数
新式服务可以拥有构造函数,与接口方法类似:
service SomeService: XSomeInterface { create1(); create2([in] long arg1, [in] string arg2); create3([in] any... rest); };
在上面的示例中,有三个显式构造函数,名为 create1
、create2
和 create3
。第一个构造函数没有参数,第二个有两个常规参数,第三个有一个特殊的 rest 参数,该参数可以接受任意数目的 any 值。构造函数参数只能为 [in]
,rest 参数必须是构造函数的唯一参数,并且必须为 any
类型;另外,与接口方法不同,服务构造函数不指定返回类型。
各个语言绑定将 UNO 构造函数映射成特定语言结构,这些结构可用于客户机代码,在给定组件上下文的情况下可得到服务实例。常规约定(例如,后跟 Java 和 C++ 语言绑定)将每个构造函数映射成同名的静态方法(resp. 函数),将 XComponentContext 作为第一个参数,后跟构造函数中指定的所有参数,并返回一个(适当键入的)服务实例。如果无法得到实例,则会抛出 com.sun.star.uno.DeploymentException。上面的 SomeService
将映射成以下 Java 1.5 类,例如:
public class SomeService { public static XSomeInterface create1( com.sun.star.uno.XComponentContext context) { ... } public static XSomeInterface create2( com.sun.star.uno.XComponentContext context, int arg1, String arg2) { ... } public static XSomeInterface create3( com.sun.star.uno.XComponentContext context, Object... rest) { ... } }
服务构造函数还拥有异常规范 ("raises (Exception1, ...)"
),其处理方法与接口方法的异常规范相同。(如果构造函数没有异常规范,则只能抛出运行时异常,尤其是com.sun.star.uno.DeploymentException。)
如果新式服务以简写形式
service SomeService: XSomeInterface;
编写,则会有一个隐式构造函数。隐式构造函数的准确行为是特定于语言绑定的,但其名称通常为 create
,除 XComponentContext 之外不接受任何参数,并且只能抛出运行时异常。
包含属性
建立 OpenOffice.org API 结构时,设计者发现办公软件环境中的对象具有大量不属于对象结构的属性,更恰当地说,它们似乎是对底层对象的表面更改。同时还发现,并非某种具体类型中的任何对象都具有所有属性。因此,没有为每种属性定义一系列复杂的可选和非可选接口,而是引入了属性的概念。属性是对象中的数据,在普通接口中按名称提供以进行属性访问,接口包含 getPropertyValue()
和 setPropertyValue()
访问方法。属性这一概念还具有其他优点,有许多信息值得了解。有关属性的详细信息,请参阅 专业 UNO - UNO 概念 - 属性。
旧式服务可直接在 UNOIDL 规范中列出支持的属性。property
定义特定类型的成员变量,可以在实现组件时按照特定名称进行访问。可以通过附加标记加入对某个 property
的进一步限制。下面的旧式服务引用了一个接口和三个可选属性。所有已知 API 类型都可以是有效的属性类型:
// com.sun.star.text.TextContent service TextContent { interface com::sun::star::text::XTextContent; [optional, property] com::sun::star::text::TextContentAnchorType AnchorType; [optional, readonly, property] sequence<com::sun::star::text::TextContentAnchorType> AnchorTypes; [optional, property] com::sun::star::text::WrapTextMode TextWrap; };
下面是可能的属性标志:
-
optional
- 实现组件时可以不支持对应的属性。
-
readonly
- 不能使用 [IDL:com.sun.star.beans.XPropertySet] 更改对应的属性值。
-
bound
- 如果任何属性值通过 com.sun.star.beans.XPropertySet 注册,则属性值的更改将通知
-
constrained
- 属性在其值被更改之前广播一个事件。侦听器有权禁止更改。
-
maybeambiguous
- 某些情况下,可能无法确定属性值,例如,具有不同值的多项选择。
-
maybedefault
- 值可能存储在某个样式工作表中,也可能存储在非对象本身的环境中。
-
maybevoid
- 除属性类型的范围以外,值也可以为空。它与数据库中的空值类似。
-
removable
- 对应的属性是可删除的,用于动态属性。
-
transient
- 如果序列化对象,将不存储对应的属性。
引用其他服务
旧式服务可以包括其他旧式服务。此类引用是可选的。一项服务被另一项服务所包含与实现继承无关,而仅仅是合并规范。由实现者决定是继承还是授权必需的功能,或者决定是否从头实现必需的功能。
以下 UNOIDL 示例中的旧式服务 com.sun.star.text.Paragraph 包含一项必需服务 com.sun.star.text.TextContent 和五项可选服务。每个 Paragraph
都必须是 TextContent
。它同时可以是 TextTable
,而且可用于支持段落和字符的格式化属性:
// com.sun.star.text.Paragraph service Paragraph { service com::sun::star::text::TextContent; [optional] service com::sun::star::text::TextTable; [optional] service com::sun::star::style::ParagraphProperties; [optional] service com::sun::star::style::CharacterProperties; [optional] service com::sun::star::style::CharacterPropertiesAsian; [optional] service com::sun::star::style::CharacterPropertiesComplex; ... };
如果上面示例中的所有旧式服务都使用多继承接口类型代替,则结构类似:多继承接口类型 Paragraph
将继承强制接口 TextContent
和可选接口 TextTable
、ParagraphProperties
等。
组件中的服务实现
组件是一个共享库或 Java 存档文件,其中包含用 UNO 支持的某种目标编程语言实现的一项或多项服务。这样的组件必须满足基本要求(目标语言不同,通常要求也不同),而且必须支持已实现的服务的规范。这意味着必须实现所有指定的接口和属性。需要在 UNO 运行时系统中注册组件。注册之后,通过在适当的服务工厂对服务实例进行排序以及通过接口访问功能,可以使用所有已实现的服务。
根据 TVSet
和 RemoteControl
服务的示例规范,组件 RemoteTVImpl
可以模拟远程电视系统:
这样的 RemoteTV
组件可以是 jar 文件或共享库。它包含两个服务实现:TVSet
和 RemoteControl
。用全局服务管理器注册 RemoteTV
组件后 ,用户就可以调用服务管理器的工厂方法,并可以请求 TVSet
或 RemoteControl
服务。然后,可以通过接口 XPower
、XChannel
和 XStandby
使用其功能。以后重新实现具有更好性能或新功能的 这些服务时,如果通过加入接口引入新功能,就可以在不破坏现有代码的情况下更换旧组件。
Content on this page is licensed under the Public Documentation License (PDL). |