使用服务
引入接口和服务的概念是出于以下几个原因:
接口和服务将规范从实现中分离出来
- 接口或服务的规范是抽象的,也就是说,它没有定义支持某项功能的对象如何在内部实现此支持。通过 OpenOffice.org API 的抽象规范,可以在 API 下分离出实现,并安装其他实现(如果需要)。
服务名称允许按规范名称而不是类名称创建实例
- 在 Java 或 C++ 中,使用 new 运算符来创建类实例。此方法有所限制:获得的类为硬编码。以后您无法在不编辑代码的情况下将其更换为其他类。服务的概念解决了这个问题。OpenOffice.org 中的中心对象工厂(即全局服务管理器)被请求创建一个对象,此对象不需要被定义内部实现就可以用于特定目的。这是可以实现的,因为通过服务名称可以从工厂定制服务,并且工厂决定返回的服务实现。获得哪种实现没有什么区别,因为您只使用明确定义的服务接口。
多继承接口使细颗粒状接口便于管理
- 如果抽象接口是细颗粒状,即小型且仅描述对象的一个方面而不是多个方面,则可以更好的重复使用。但那样的话,就需要多个此类接口来描述一个有用的对象。一方面,多继承接口允许具有细颗粒状接口;另一方面,又允许通过将接口组织成集合来轻松地管理这些接口。由于在办公软件环境中对象很可能共享多个方面,因此,此细颗粒状可以使接口重复使用,从而获得行为一致的对象。例如,可以使用统一的方式来处理文字,无论处理的是正文、文字框、页眉或页脚文字、脚注、表格单元格还是绘图形状中的文字。不需要为这些不同的文字处理定义单独的接口。
我们来看看 UML 表示法中的旧式服务 com.sun.star.text.TextDocument。下面的UML 图描述了 TextDocument
服务的强制接口。这些接口表现了 OpenOffice.org 中文本文档的基本方面。图中含有文字,可进行搜索和刷新。它是包含 URL 和控制器的模型,并且可进行修改、打印和存储。UML 图显示了如何在 API 中进行指定。
左侧显示了服务 com.sun.star.text.TextDocument 和
com.sun.star.document.OfficeDocument。每个 TextDocument
都必须通过定义包含这些服务。
右侧显示了服务必须导出的接口。其方法分层列出了各种接口中含有的方法。在 OpenOffice.org 中,所有接口的名称都必须以 X 开头,以区别于其他实体名称。
每个 TextDocument
都必须支持以下三种接口:XTextDocument
、XSearchable
和 XRefreshable
。此外,由于 TextDocument
始终是 OfficeDocument
,因此它还必须支持
XPrintable
、XStorable
、XModifiable
和 XModel
接口。这些接口中含有的方法涉及:打印、存储、修改和模型处理等方面。
请注意,插图中显示的接口仅为 TextDocument
的强制接口。TextDocument
具有可选属性和接口,其中,必须支持 CharacterCount
、ParagraphCount
和 WordCount
属性以及 XPropertySet
接口(如果属性存在)。OpenOffice.org 中 TextDocument
服务的当前实现不仅支持这些接口,而且支持
所有可选接口。TextDocument 的用法在文本文档中进行了详细的介绍。
使用接口
由于必须通过 UNO 对象的接口来访问它们,因此会对某些语言(例如 Java 和 C++)造成影响。因 为在这些语言中,只有编译器所需的对象引用类型正确时才能从中调用方法。在 Java 或 C++ 中,在 访问对象实现的接口之前,通常就可以转换对象类型。但使用 UNO 对象时是不同的:必须, UNO 环境在需要访问对象支持的接口的方法时获得相应的引用,但编译器对此不知情。只有这时才能安 全地转换对象类型。
Java UNO 环境含有用于此目的的 queryInterface()
方法。它看起来似乎非常复杂,但当您了解了 queryInterface()
是与跨进程安全转换 UNO 类型相关的,就会很快习惯它。看一下第二个示例 FirstLoadComponent ,其中创建了一个新 Desktop 对象,然后使用 queryInterface 方法获取 XComponentLoader 接口。
Object desktop = xRemoteServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", xRemoteContext); XComponentLoader xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktop);
我们请求服务管理器使用其工厂方法 createInstanceWithContext() 创建
com.sun.star.frame.Desktop 接口。此方法定义为返回 Java 对象类型,您对此应不会感到意外,因为工厂必须能够返回任何类型:
java.lang.Object createInstanceWithContext(String serviceName, XComponentContext context)
我们收到的对象是 com.sun.star.frame.Desktop 服务。
以下为 UML 表示法中的一个简单规范,显示与 com.sun.star.frame.Frame 服务及支持的接口之
间的关系。问题是,我们知道在工厂中定制的对象是 DesktopUnoUrlResolver,并从其他接口中导
出接口 XComponentLoader
,而编译器却不知道。因此,必须使用 UNO 运行时环境请求或查询 XComponentLoader
接口,因为我们要使用此接口上的 loadComponentFromURL()
方法。queryInterface()
方法可以确保获得能够转换为所需接口类型的引用,无论目标对象是本地对象还是远程
对象。Java UNO 语言绑定中有两种 queryInterface()
定义:
java.lang.Object UnoRuntime.queryInterface(java.lang.Class targetInterface, Object sourceObject) java.lang.Object UnoRuntime.queryInterface(com.sun.star.uno.Type targetInterface, Object sourceObject)
由于指定 UnoRuntime.queryInterface()
返回 java.lang.Object(与工厂方法 createInstanceWithContext()
相同),因此仍必须将接口引用明确地转换为所需的类型。不同之处在于,使用 queryInterface()
可以将对象安全转换为接口类型,而且最重要的是,引用现在甚至可以与另一个进程中的对象一起使用。以下是分步详解的 queryInterface()
调用:
XComponentLoader xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktop);
XComponentLoader
是我们要使用的接口,因此定义名为 xComponentLaoder(x 小写)的 XComponentLoader
变量,以存储 queryInterface
中的接口。
然后,查询 desktop 对象中的 XComponentLoader
接口(将 XComponentLoader.class
作为目标接口传入,并将 desktop 作为源对象传入)。最后,将结果转换为 XComponentLoader
,并将生成的引用指定给变量 xComponentLoader。
如果源对象不支持查询的接口,queryInterface()
将返回 null。
在 Java 中,当引用的对象支持所需接口但引用类型不正确时,对 queryInterface()
的此调用是必需的。幸运的是,您不仅可以从 java.lang.Object
源类型调用 queryInterface()
,而且还可以从其他接口引用查询接口,如下所示:
// loading a blank spreadsheet document gives us its XComponent interface: XComponent xComponent = xComponentLoader.loadComponentFromURL( "private:factory/scalc", "_blank", 0, loadProps); // now we query the interface XSpreadsheetDocument from xComponent XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument)UnoRuntime.queryInterface( XSpreadsheetDocument.class, xComponent);
此外,如果以此方式将方法定义为已经返回接口类型,则无需查询接口,但是却可以立即使用其方
法。在以上代码中,loadComponentFromURL
方法指定为返回 com.sun.star.lang.XComponent 接口,因此如果希望收到正在关闭文档的通知,可以直接在 xComponent
变量中调用 XComponent 方法
addEventListener()
方法和 removeEventListener()
方法。
C++ 中相应的步骤由 Reference<>
模板完成,该模板将源实例作为参数对待:
// instantiate a sample service with the servicemanager. Reference< XInterface > rInstance = rServiceManager->createInstanceWithContext( OUString::createFromAscii("com.sun.star.frame.Desktop" ), rComponentContext ); // Query for the XComponentLoader interface Reference< XComponentLoader > rComponentLoader( rInstance, UNO_QUERY );
在 OpenOffice.org Basic 中,不需要查询接口,Basic 运行时引擎在内部可以完成相关操作。
随着多继承接口在 OpenOffice.org API 中的扩展,在 Java 或 C++ 中明确地查询特定接口的需求将越来越少。例如,用假定接口
interface XBase1 { void fun1(); }; interface XBase2 { void fun2(); }; interface XBoth { // inherits from both XBase1 and XBase2 interface XBase1; interface XBase2; }; interface XFactory { XBoth getBoth();};
您可以在通过 XFactory.getBoth() 方法获得的引用中直接调用 fun1()
和 fun2()
,而无需查询 XBase1
或 XBase2
。
使用属性
一个对象必须通过那些允许您使用属性的接口来提供它的属性。这些接口最基本的形式为接口
com.sun.star.beans.XPropertySet。还有属性的其他接口,例如
com.sun.star.beans.XMultiPropertySet,它可以用单个方法调用来获取和设置多个属性。当服务中存在属性时,始终支持 XPropertySet
。
在 XPropertySet
中,有两种方法执行属性访问,这两种方法在 Java 中定义如下:
void setPropertyValue(String propertyName, Object propertyValue) Object getPropertyValue(String propertyName)
在 FirstLoadComponent 示例中XPropertySet
接口用于设置单元格对象的 CellStyle 属性。该单元格对象是一个 com.sun.star.sheet.SheetCell
,从而也支持 com.sun.star.table.CellProperties
服务,该服务具有属性 CellStyle。以下代码解释如何设置此属性:
// query the XPropertySet interface from cell object XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xCell); // set the CellStyle property xCellProps.setPropertyValue("CellStyle", "Result");
现在,您可以开始使用 OpenOffice.org 文档了。
Content on this page is licensed under the Public Documentation License (PDL). |