Skip to content
Fabio Simeoni edited this page Aug 19, 2014 · 7 revisions

Model entities have common traits, both in state and behaviour.

Traits project similarities on public and private APIs, as well as on bean SPIs. So, we factor them out in dedicated components.

Traits are complementary to containers as mechanisms for code reuse in the model.

Traits include:

  • Identified, Identified.Private, and Identified.Bean define and implement APIs for identification, bean delegation, and reified updates.

  • Named, Named.Private, and Named.Bean define and implement the API for accessing and updating names.

  • Described, Described.Private, and Described.Bean define and implement the API for accessing and updating attributes.

  • Definition, Definition.Private, and Definition.Bean define and implement the API for accessing and updating occurrence ranges. We reuse it in entities that have instances, such as attribute definitions and link definitions.

  • Defined and Defined.Bean define the API for accessing and updating the entity definitions. We reuse it in attributes and links.

Note that the API is not implemented as a trait, i.e. there is no Defined.Private available for reuse. This is because links are described while attributes are not, we cannot arrange them in a single hierarchy. Here we hit the limit of single class inheritance and our ability to emulate real implementation traits (see below).

  • BeanOf is an interface for beans that act as entity factories. It is a technical trait used solely in containers and justified in detail there.

The public APIs of traits are pairwise independent. We combine them in the public APIs of concrete entities, e.g.:

interface Codelist extends Identified, Named, Attributed {
...
}

We cannot do the same for private APIs. Since they are classes, we can only reuse them if we arrange them in a hierarchy:

abstract class Named.Private ... extends Named.Private implements Named {...}
...
abstract class Described.Private ... extends Named.Private implements Described {...}
...
abstract class Codelist.Private ... extends Described.Private ... implements Codelist {...}

note: the private API of traits are all abstract.

We also deliberately arrange bean SPIs in an hierarchy, even through they are interfaces. This is for convenience, as it simplifies the parametrisation of private APIs as well as SPI implementations.

interface Named.Bean ... extends Identified.Bean ... {...}
...
interface Described.Bean ... extends Named.Bean ... {...}
...
interface Codelist.Bean ... extends Described.Bean ... {...}

What's with the generics?

The private APIs of traits make heavy use of generics, which deserves some clarification.

Identified.Private introduces two type parameters:

interface Identified {
  ...
  interface Bean {...}
  ...
  abstract class Private<SELF extends Private<SELF,B>, B extends Bean> ... {...}
}

The second parameter B is fairly straightforward, so we begin from there. B stands for the state bean of the entity, which Identified.Private holds and exposes on behalf of subclasses. We use B to avoid type loss when we return the bean to clients, particularly to the subclasses themselves:

private final B bean;

protected Private(B bean) {
 this.bean=bean;    
}

public B bean() {
 return bean;
} 

We also specify Identified.Bean as the upper bound of B because we need to access its identifier within Identified.Private itself.

The first parameter SELF is less obvious, it stands for the generic subclass that will inherit from Identified.Private. We use it here to avoid type loss in the base implementation of the API for reified updates:

public update(SELF changeset) {...}

This is a covariant binary method, i.e. it's meant to take another instance of the class that declares it. We want it inherited in any subclass so that it takes an instance of that subclass. In Codelist.Private, for example, we want to override it as follow:

public update(Codelist.Private changeset) {
 super.update(changeset);
 ...
}

Up here in Identified.Private we can only use some placeholder for Codelist.Private or whatever the subclass will be. Since Java has no special support for this, we emulate it with a type parameter that subclasses will need to instantiate to themselves. For example, if Codelist.Private derived directly Identified.Private (it doesn't), it would instantiate SELF as follows:

class Codelist.Private extends Identified.Private<Codelist.Private,...> {...}

The tong-twisting upper bund on SELF is our best attempt to say "it must extend Identified.Private and have itself as a parameter". This it's not as tight as we like, but it's our best approximation of a true self type as found in other (non-mainstream) languages.

Of course, the traits below Identified.Private carry over the pattern:

interface Named {
  ...
  interface Bean ... {...}
  
  abstract class Private<SELF extends Private<SELF,B>, B extends Bean> ... {...}
}

interface Described {
  ...
  interface Bean ... {...}
 
  abstract class Private<SELF extends Private<SELF,B>, B extends Bean> ... {...}
}

until we hit the private API of an entity, where we finally instantiate both parameters. For Codelist.Private:

interface Codelist ... {
  ...
  interface Bean ... {...}
  
  class Private extends Described.Private<Private,Bean> ... {...}
}
Clone this wiki locally