跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

模式作用域:初级读本和最佳实践

了解模式设计的关键方面

Casey D Jordan, 所有者, Jorsek
Casey D. Jordan 的照片
Casey Jordan 是结构化/灵巧数据的传道者,还是 Jorsek 的共同创办人,该公司提供软件和服务来改善组织的数据部署、存储和人员协作的质量。Casey 在内容管理和 web 服务方面具有 10 多年的经验,他帮助公司采用 XML 技术的内容策略,比如原生 XML 数据库、XQuery、XSLT、XML Schema 和 DITA。
Dale Waldt, 高级顾问, aXtive Minds
Dale Waldt 的照片
Dale Waldt 在领导 XML 应用程序、复合和发布解决方案以及各种复杂 Web 站点(面向政府、商业和非盈利机构)的设计和开发方面拥有超过 25 年的经验。Dale 经常和各种开发团队工作,这些团队的工作包括优化流程、设计模式、领导数据和应用程序设计和开发、评估软件和服务,以及为开发人员培训 XML、XSLT 和相关技术。在过去 10 年中,作为一名顾问、讲师和行业分析师,他主要关注 Web、内容技术和开放标准的采用。

简介: XML 模式定义语言(XML Schema Definition Language,XSDL)对于建模和验证数据是一个极好的工具。模式作用域在模式设计中至关重要,但是常常被忽视。模式的元素、类型和属性可以具有局部或全局作用域,作用域的选用会影响模式的可重用性、可交互性和生命周期。本文解释局部和全局作用域模式构造的定义和使用,并提供为模式选择最佳作用域设计的技巧和最佳实践。

发布日期: 2010 年 10 月 18 日
级别: 中级 其他语言版本: 英文
访问情况 : 3864 次浏览
评论: 


模式 是一个格式良好的 XML 文档,它使用强大的 XML 模式定义语言(XSDL,有时也叫做 W3C Schema)来建模和验证其他 XML 数据。根据您是如何定义的,模式粒子(元素、类型、属性和其他构造)都具有一个相关的作用域,可以是全局/暴露的,也可以是局部/隐藏的。模式的作用域设计极大地影响了模式可以如何被改进、重用以及与其他技术协作。

无论您是刚开始使用模式,还是想要更大地发挥当前解决方案的作用,理解模式作用域都是您成功的关键。在本文中,我们首先展示如何为各种模式粒子定义全局或局部作用域,并解释作用域将如何影响它们的行为。然后我们将描述基本的模式设计范式,并将探索创建满足项目需求的作用域设计方面的考虑因素和最佳实践。

常用缩略词

  • W3C:万维网联盟
  • XML:可扩展标记语言
  • XSDL:XML 模式定义语言

定义全局作用域元素

模式的最高层容器元素是 schemaschema 元素的直接子元素是全局定义的(就是说,具有全局作用域)。您可以使用全局元素作为根节点,并且可以从模式的其他部分引用它们。元素只要定义一次,您然后就可以在整个模式中使用它。

清单 1 中的模式例子展示了一个简单的数据模型,带有一个名叫 postalCode 的全局元素:


清单 1. 带有单个全局元素的模式
				
<xs:schema>
 <xs:element name='postalCode' type='xs:string'/>
</xs:schema>

可以使用 清单 1 中的模式来成功地验证以下数据实例:

<postalCode>14534</postalCode>

在这个数据实例中,postalCode根元素 — 即数据实例中的最高层容器。只有在相关模式的最高层定义的元素才可以充当数据实例中的根元素。清单 1 中的模式只定义了一个元素,所以很容易理解只有 postalCode 可以充当实例中的根元素。

清单 2 中的例子模式在根层次定义了两个元素:


清单 2. 带有两个可能根元素的模式
				
<xs:schema>
  <xs:element name='postalCode' type='xs:string'/>
  <xs:element name='zipCode' type='xs:string'/>
</xs:schema>

postalCodezipCode 都可以充当 清单 2 中模式所建模的实例中的根元素。


定义局部作用域元素

将元素定义为局部的可以防止它们被暴露给模式的其他部分。局部元素的上下文局限于它的当前位置,所以不能从模式的其他部分引用它。在 清单 3 的例子中,zipCode 元素不是全局定义的。相反,它被作为 address 元素的子元素,定义在一个元素定义的 complexType 中。


清单 3. 单个带有局部子元素的全局元素
				
<xs:schema>
<xs:element name='address'>
 <xs:complexType>
  <xs:sequence>
   <xs:element name='street' type='xs:string'/>
   <xs:element name='city' type='xs:string'/>
   <xs:element name='state' type='xs:string'/>
   <xs:element name='zipCode' type='xs:string'/>
  </xs:sequence>
 </xs:complexType>
</xs:schema>

由于 zipCode 元素的定义位于 address 元素的声明中,所以它是一个局部定义,它的作用域只在 address 元素中。对于有效的文档实例,zipCode 元素必须出现在 address 元素中,如 清单 4 所示:


清单 4. 清单 3 中模式的有效数据实例
				
<address>
 <!-- street, city and state hidden for example purposes -->

 <zipCode>14534</zipCode>
</address>

清单 4 中,address 元素是根元素。zipCode 元素不能充当实例中的根元素,因为它不是在模式模块中的根层次被全局定义的。局部定义的元素只能出现在定义它们的元素定义上下文中。


在局部作用域内引用全局元素和属性

除了充当根元素之外,任何全局定义的元素也可以在任何需要它们的局部作用域内被引用和出现。在 清单 5 的例子中,全局定义的 zipCode 元素被用于局部作用域上下文中的 address 元素的定义中:


清单 5. 局部作用域内引用的全局元素
				
<xs:schema>
 <xs:element name="address">
  <xs:complexType>
   <xs:sequence>
    <xs:element name='street' type='xs:string'/>
    <xs:element name='city' type='xs:string'/>
    <xs:element ref='zipCode'/> <!-- reference to globally defined element -->
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
<!-- Globally defined element that is referenced in element above -->
 <xs:element name='zipCode' type='xs:string'/>
</xs:schema>

您可以看到,全局暴露的元素声明支持模块化和重用。您可以在该模式的其他部分和会导入该模式的父模式中引用 zipCode 元素。

属性定义与此类似。例如,在 清单 6 中,全局定义的 state 被引用在局部作用域上下文中的 address 元素中:


清单 6. 局部作用域中引用的全局属性
				
<xs:schema>
 <xs:element name='address'>
  <xs:complexType>
   <!--[.. elements removed for readability..]-->
   <xs:attribute ref="state"/> <!-- referencing globally defined attribute -->
  </xs:complexType>
 </xs:element>

 <xs:attribute name="state" type="xs:string"/> <!--globally defined attribute -->

</xs:schema>


类型定义

就跟您可以全局和局部地定义元素和属性一样,您也可以这样定义类型。前面的例子为 address 元素定义使用了局部定义的类型。要让该类型定义成为全局的,可将它从局部定义删除,取一个独特的名称,并将它放在根 schema 节点下,如 清单 7 所示:


清单 7. 局部作用域内引用的全局类型
				
<xs:schema>
 <xs:element name='address' type="address.type"/>
 
  <xs:complexType name="address.type">
   <xs:sequence>
    <xs:element name='street' type='xs:string'/>
    <xs:element name='city' type='xs:string'/>
    <xs:element name='state' type='xs:string'/>
    <xs:element name='zipCode' type='xs:string'/>
   </xs:sequence>
 </xs:complexType>
</xs:schema>

此类型定义现在是全局的了,并且具有独特的名称 address.type。要将该类型与一个元素相关联,我们通过将类型属性(type="")与全局类型名称相关联而引用它。您可以通过使用 xs:extension 元素扩展全局类型定义,也可以通过使用 xs:restriction 元素限制它。


基本设计范式

确定应该将模式粒子定义为具有局部作用域还是全局作用域并不总是那么容易。根据使用情况、名称空间需求和模式进化等因素,最佳选择也各不一样。一般来说,模式设计归类为四种基本范式:

  • Russian doll(俄罗斯套娃)
  • Salami slice(意大利香肠片)
  • Venetian blinds(软百叶窗)
  • Garden of Eden(伊甸园)

要为自己的项目确定最佳解决方案,一定要理解这些范式。

Russian doll 范式

该范式来源于著名的俄罗斯套娃(Russian doll)— 一套尺寸由大到小的木头娃娃,一个套一个地装在一起。Russian doll 范式局部地定义所有子元素;因此,每个元素及其类型都封装在其父元素之中,就像俄罗斯套娃那样。

清单 8 中的例子 — 一种家电的帮助文档的简化表示 — 演示了该范式:


清单 8. Russian doll 风格的模式
				
<xs:schema>
<xs:element name="HelpDoc">
<xs:complexType>
 <xs:sequence>    
  <xs:element name="Section">    
   <xs:complexType>
    <xs:sequence>    
     <xs:element name="Title" type="xs:string"/>    
     <xs:element name="Body" type="xs:string"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:string"/>
   </xs:complexType>
  </xs:element>
 </xs:sequence>
 <xs:/complexType>
</xs:element>
</xs:schema>

一个相关的实例类似于 清单 9 这样。


清单 9. 匹配 Russian doll 模式模型的实例
				
<HelpDoc>
 <Section name="operation_instructions">
  <Title>Operating your appliance.</Title>
  <Body>First, open the packaging and check to see...</Body>
 </Section>
</HelpDoc>

快速技巧:名称空间

当一个模式被确定名称空间时(就是说,具有 targetNamespace),所有全局粒子都必须用全限定名称进行引用(即 prefix:name)。这样的名称空间被称为是暴露的。例如:

<xs:schema xmlns:xyz="http://xyzcompany.com" 
  targetNamespace="http://xyzcompany.com">
  
  <xs:element name="HelpDocs">
   <xs:complexType>
    <xs:sequence>    
     <!--   Global element is referenced,      -->
           <!--   must contain namespace prefix.     -->
     <xs:element ref="xyz:Section"/> 
    </xs:sequence>
   </xs:complexType>
  </xs:element>
  
  <xs:element name="Section">    
   <!--        -->
  </xs:element>
  
  </xs:schema>

由于 Section 元素是全局的,并且声明了一个 targetNamespace,所以在对 Section 元素的引用中,xyz 名称空间前缀是必需的。<xs:element ref="Section"/> 不是有效的引用。

您可以看到,清单 8 中的每个子元素、属性和类型都是局部定义的。惟一的全局元素是根元素,即 HelpDoc。该语法紧凑,有些人可能会觉得它容易理解。Russian doll 风格的模式不将它们的组件暴露给其他类型、元素或模式,所以它们也被认为是高度去耦的(就是说,元素不全局地依赖于其他元素)和内聚的(相关元素被分组在单个自包含的父元素中)。

此范式概括为这样一种模式,即很少有跟其他系统的交互,其组件也没有重用。通过以这种方式定义模式,您可以保持结构为自包含的、隐藏名称空间以及防止受到其他系统的影响。

Salami slice 范式

利用 Salami slice 范式,您可进入暴露内容模型的下一步。在该范式中,您可以将自己所有局部定义的元素都移动到全局定义中。清单 10 展示了 清单 8 中 Russian doll 风格例子修改为满足 Salami slice 范式后的样子:


清单 10. Salami slice 范式
				
<xs:schema>
 <xs:element name="Body" type="xs:string"/>
 <xs:element name="Title" type="xs:string"/>   

<xs:element name="Section">    
 <xs:complexType>
  <xs:sequence>    
   <xs:element ref="Title"/>    
   <xs:element ref="Body"/>
  </xs:sequence>
  <xs:attribute name="name" type="xs:string"/>
 </xs:complexType>
 </xs:element>

 <xs:element name="HelpDocs">
  <xs:complexType>
   <xs:sequence>    
    <xs:element ref="Section"/>
   </xs:sequence>
  </xs:complexType>
 </xs:element>
</xs:schema>

Salami slice 范式暴露所有元素,所以您可以在模式的其他部分引用和重用它们,并且它让它们对其他模式是透明的。该方法一个主要的优点是,元素高度可重用。但是,这也意味着所有名称空间都是全局暴露的,并且元素之间的耦合增大了。在 清单 10 中,Section 元素与 TitleBody 元素是全局耦合的。对 TitleBody 元素的任何修改随后都会影响 Section 定义。

Venetian blinds 范式

在 Venetian blinds 范式中,不是全局地定义所有元素,您首先是全局地定义所有类型,如 清单 11 中的例子所示:


清单 11. Venetian blinds 范式
				
<xs:schema>
  <xs:complexType name="section.type">
   <xs:sequence>    
    <xs:element name="Title" type="xs:string"/>    
    <xs:element name="Body" type="xs:string"/>
   </xs:sequence>
   
   <xs:attribute name="name" type="xs:string"/>
  </xs:complexType>
  
  <xs:complexType name="helpdocs.type">
   <xs:sequence>    
    <xs:element name="Section" type="section.type"/>
   </xs:sequence>
  </xs:complexType>
  
  <xs:element name="HelpDocs" type="helpdocs.type">
   
</xs:schema>

Venetian blinds 风格使用全局类型定义来增加重用能力。由于所有子元素都是局部的,所以它额外的优势是能够隐藏名称空间。该方法允许您在使用 elementFormDefault 属性作为隐藏或暴露名称空间的一个切换开关的同时,暴露您的结构定义以便重用。您做到了两全其美!

Garden of Eden 范式

在 Garden of Eden 设计范式中,您让元素声明和类型声明都是全局的,将全局化发挥到了极致。清单 12 展示了一个 Garden of Eden 风格的模式:


清单 12. Garden of Eden 范式
				
<xs:schema>
 <xs:attribute name="name" type="xs:string"/>
 <xs:element name="Title" type="xs:string"/>    
 <xs:element name="Body" type="xs:string"/>
 <xs:element name="Section" type="section.type"/>
 <xs:element name="HelpDocs" type="helpdocs.type">

 <xs:complexType name="section.type">
  <xs:sequence>    
   <xs:element ref="Title"/>    
   <xs:element ref="Body"/>
  </xs:sequence>
  <xs:attribute ref="name"/>
 </xs:complexType>

 <xs:complexType name="helpdocs.type">
  <xs:sequence>    
   <xs:element ref="Section"/>
  </xs:sequence>
 </xs:complexType>
         
</xs:schema>

通过让每个可能的元素、属性和类型都成为全局的,您创建了这样一种场景,即无论是在内部还是在模式之间都最大化了重用 — 尽管是通过强迫名称空间为暴露的。通过完全暴露您的结构,您让模式高度耦合却敏捷。由于元素是相互依赖的,所以很快就会应用大规模的更改。


最佳实践:模式管理和改进

设计模式时,您通常会在暴露可重用组件、隐藏名称空间和限制名称空间暴露以及降低耦合(或者叫做多个全局元素/类型之间的相互依赖)之间取得平衡。图 1 总结了四种模式范式各自的重用潜能,指出了它们耦合和暴露方面的相对等级:


图 1. 暴露和与不同范式的耦合
图中说明了暴露和与四种模式范式的耦合

为重用模式组件提供高潜能可以缩短未来的开发时间和让大规模更改变得容易。但是,它也会带来这样的情况,即多个元素和类型不必要地耦合在一起。当模式变得高度耦合时,元素和类型就变得相互依赖,使得难以管理未来的更改和添加。耦合的猖獗会阻止模式的改进,因为其他系统依赖于您的接口保持一致。对于暴露什么和暴露多少,一定要谨慎。一旦做出选择,就难以改变。

不过,还是有一些方法可以确保未来的成功。首先是适当的作用域设计:

  • 如果模式重用不是特别重要,而最小化代码大小却很必要,那么请使用 Russian doll 风格,因为它很紧凑,可以用来最大化名称空间隐藏。
  • 如果元素替代对于您的设计很重要,或者您需要让元素对其他模式是透明的,那么请使用 Salami slice 风格或 Garden of Eden。
  • 如果您想要高度的重用,还想最大可能地隐藏名称空间,那么请使用 Venetian blinds 风格。然后可以使用 elementFormDefault 作为一个切换开关,来要求暴露或隐藏元素。

此外,不要害怕混用。在特定的模式中使用多种模式设计范式是高度有益的。对于您希望保持为私有和隐藏的结构部分,使用 Russian doll 风格。同时,您也可能想要使用 Salami slice 或 Garden of Eden 设计全局地暴露一些元素。例如,清单 13 使用了高度暴露的 Garden of Eden 风格和隐藏的 Russian doll 风格:


清单 13. 混用范式
				
<xs:schema>
  <!-- Garden of Eden style component    -->
  <xs:element name="Title" type="xs:string"/>    
  <xs:element name="Body" type="xs:string"/>
  <xs:element name="Section" type="section.type"/>
  <xs:element name="HelpDocs" type="helpdocs.type">
   
  <xs:complexType name="section.type">
   <xs:sequence>    
    <xs:element ref="Title"/>    
    <xs:element ref="Body"/>
   </xs:sequence>
   
   <xs:attribute name="name"/>
  </xs:complexType>
  
  <xs:complexType name="helpdocs.type">
   <xs:sequence>    
    <xs:element ref="Section"/>
    <!--    Russian doll style component    -->
    <xs:element ref="Credits">
     <xs:complexType>
      <xs:sequence>    
       <xs:element name="Author" type="xs:string"/>    
       <xs:element name="Year" type="xs:string"/>
      </xs:sequence>
     </xs:complexType>
    </xs:element>
   </xs:sequence>
  <xs:complexType>       
</xs:schema>

在此场景中,也许您需要暴露 HelpDocsSectionTitleBody 元素,以便其他模式使用。但是,您想要隐藏 Credits 元素,以防止它与其他模式定义耦合。

如果重用的需求不是十分迫切,那么用这种方式编写模式是完全合理的。通过让元素成为全局的,很容易增加暴露。后面去掉暴露将会很困难。在这种情况下,如果未来设计需要更多的暴露,您可以很容易增加。


结束语

在开始任何模式项目之前,您必须做出符合自己目标的设计选择。通过理解模式作用域的使用,可以流水线化管理模式和内容的过程。最终,这将提升您的能力,来管理模式生命周期和让您的模式高效地与其他系统交互。


参考资料

学习

获得产品和技术

讨论

作者简介

Casey D. Jordan 的照片

Casey Jordan 是结构化/灵巧数据的传道者,还是 Jorsek 的共同创办人,该公司提供软件和服务来改善组织的数据部署、存储和人员协作的质量。Casey 在内容管理和 web 服务方面具有 10 多年的经验,他帮助公司采用 XML 技术的内容策略,比如原生 XML 数据库、XQuery、XSLT、XML Schema 和 DITA。

Dale Waldt 的照片

Dale Waldt 在领导 XML 应用程序、复合和发布解决方案以及各种复杂 Web 站点(面向政府、商业和非盈利机构)的设计和开发方面拥有超过 25 年的经验。Dale 经常和各种开发团队工作,这些团队的工作包括优化流程、设计模式、领导数据和应用程序设计和开发、评估软件和服务,以及为开发人员培训 XML、XSLT 和相关技术。在过去 10 年中,作为一名顾问、讲师和行业分析师,他主要关注 Web、内容技术和开放标准的采用。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=551459
ArticleTitle=模式作用域:初级读本和最佳实践
publish-date=10182010
author1-email=casey.jordan_cnnew1@jorsek.com
author1-email-cc=
author2-email=dale_cnnew1@axtiveminds.com
author2-email-cc=

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。