W3C home > Mailing lists > Public > xmlschema-dev@w3.org > April 2002

Re: Schema Design: Composition vs Subclassing

From: Jeni Tennison <jeni@jenitennison.com>
Date: Tue, 16 Apr 2002 17:00:15 +0100
Message-ID: <361239990722.20020416170015@jenitennison.com>
To: "Paul Kiel" <paul@hr-xml.org>
CC: xmlschema-dev@w3.org
Hi Paul,

> I have a question about the effect of this best practice. It is
> regarding context-specific use of components. Let's take the element
> <Person> in a human resources context. (BTW - <Person> is too broad
> a concept to actually encode, this is only for discussion). We may
> want to use a <Person> in many contexts, where some of its
> components are required in one and not in another. Let's say we have
> two transactions of Person below (and that all children are stand
> alone components):
>
> Transaction 1:
> <Person>
>      <Name/><!-- required -->
>      <Skills/><!-- required -->
>      <Height/><!-- required -->
>      <Weight/><!-- required -->
> </Person>
> In this transaction, we need all the data about this person to do
> the transaction.
>
> Transaction 2:
> <Person>
>      <Name/><!-- required -->
>      <Skills/><!-- optional -->
> </Person>
> In this transaction, we only need a name of the person and the
> skills are optional. The Height and Weight have no meaning in this
> context and can't occur.

None of the methods that you suggested seem particularly good to me.
In the example above, you have one thing that stays the same (<Person>
always has a <Name> element child), and two things that change
(whether <Skills> is required or optional, and whether the <Person>
includes a <Height> and <Weight>).

If you can take advantage of treating all Person elements in the same
way when it comes to their Name (i.e. that you can get some code reuse
out of it), I'd make a general PersonType that included a <Name>
element:

<xs:complexType name="PersonType" abstract="yes">
  <xs:sequence>
    <xs:element name="Name" type="xs:string" />
  </xs:sequence>
</xs:complexType>

I'd then create types that extend this base type. For Transaction 1:

<xs:complexType name="Transaction1PersonType">
  <xs:extension base="PersonType">
    <xs:sequence>
      <xs:element name="Skills" type="SkillsType" />
      <xs:element name="Height" type="xs:decimal" />
      <xs:element name="Weight" type="xs:decimal" />
    </xs:sequence>
  </xs:extension>
</xs:complexType>

<xs:complexType name="Transaction2PersonType">
  <xs:extension base="PersonType">
    <xs:sequence>
      <xs:element name="Skills" type="SkillsType" minOccurs="0" />
    </xs:sequence>
  </xs:extension>
</xs:complexType>

If you can't take advantage of the fact that the Person elements in
Transaction1 and Transaction2 are similar (i.e. for some reason you
can't share code between them) then you could design through
composition instead. This time the shared components should go into
groups:

<xs:group name="NameGroup">
  <xs:sequence>
    <xs:element name="Name" type="xs:string" />
  </xs:sequence>
</xs:group>

<xs:group name="SkillsGroup">
  <xs:sequence>
    <xs:element name="Skills" type="SkillsType" />
  </xs:sequence>
</xs:group>

<xs:group name="HeightAndWeightGroup">
  <xs:sequence>
    <xs:element name="Height" type="xs:decimal" />
    <xs:element name="Weight" type="xs:decimal" />
  </xs:sequence>
</xs:group>

Then you could have two (possibly anonymous) types that bring those
groups together as required:

<xs:complexType name="Transaction1PersonType">
  <xs:sequence>
    <xs:group ref="NameGroup" />
    <xs:group ref="SkillsGroup" />
    <xs:group ref="HeightAndWeightGroup" />
  </xs:sequence>
</xs:complexType>

<xs:complexType name="Transaction2PersonType">
  <xs:sequence>
    <xs:group ref="NameGroup" />
    <xs:group ref="SkillsGroup" minOccurs="0" />
  </xs:sequence>
</xs:complexType>

A third possibility would be to have an abstract version of the
PersonType that includes a group with nothing in it as a placeholder:

<xs:complexType name="PersonType" abstract="yes">
  <xs:sequence>
    <xs:element name="Name" type="xs:string" />
    <xs:group ref="PersonGroup" />
  </xs:sequence>
</xs:complexType>

<xs:group name="PersonGroup">
  <xs:sequence />
</xs:group>

Then, in the schema for Transaction 1, you can redefine the
PersonGroup group to add the required elements:

<xs:redefine href="baseSchema">
  <xs:group name="PersonGroup">
    <xs:sequence>
      <xs:group ref="PersonGroup" />
      <xs:element name="Skills" type="SkillsType" />
      <xs:element name="Height" type="xs:decimal" />
      <xs:element name="Weight" type="xs:decimal" />
    </xs:sequence>
  </xs:group>
</xs:redefine>

and similarly in the schema for Transaction 2:

<xs:redefine href="baseSchema">
  <xs:group name="PersonGroup">
    <xs:sequence>
      <xs:group ref="PersonGroup" />
      <xs:element name="Skills" type="SkillsType" minOccurs="0" />
    </xs:sequence>
  </xs:group>
</xs:redefine>

Personally, I think I'd favour 1, but I'm in the process of being
persuaded towards composition, so I reserve the right to change my
mind.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/
Received on Tuesday, 16 April 2002 12:00:21 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Tuesday, 11 January 2011 00:14:30 GMT