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

Most model entities are associated with one or more other entities. Codelists have codes and definitions, codes have links, and everything has attributes.

We maintain these associations in containers, i.e. custom collection components that:

  • propagate entity API calls to their elements, primarily for update
  • follow the same API separation we use for entities.

Containers are "model-friendly" collections.

Containers are complementary to traits as mechanisms for code reuse in the model.

The relevant components:

  • Container defines the public API of containers.

It extends Iterable with a small read-only API. We can measure size, lookup elements (by identifier or by name) or check for their existence. Some test-oriented methods for added fluency and that's it.

  • Container.Private implements Container and defines the private API of containers.

The key piece of code here is the implementation of the update API over the elements of the container. Here we handle element addition and removal on behalf of the entity that hosts the container.

  • like all private APIs, Container.Private delegates state access through the bean SPI, here Container.Bean.

Container.Bean includes mutators required by Container.Private to implement the update API. We've been careful not to assume a materialised bean collection underneath (e.g. with multi-id lookups).

  • Attributes, Codes, Links, AttributeDefinitions, and LinkDefinitions extend Container.Private for specific element types. The extensions are trivial in most cases (remove type parameters), but serve as placeholders for future API customisations. Attributes already adds extra attribute-specific methods that we could not place on the agnostic API of Container.Private.

##What's with the generics?

Container.Private relies on generics:

interface Container<E> ... {
  ...
  interface Bean ... {...}

  class Private<E extends Named.Private<E,B>, 
                B extends BeanOf<E> & Named.Bean> implements Container<E>
}

That's quite a mouthful. It shows us a big advantage of separating public and private APIs: hide complexity from clients in the private API. Here we prevent generics from polluting client code and keep the public API as simple as a standard Collection API.

About the parameters:

  • E stands for the type of elements. The upper bound makes it clear that we need named entities with their private API.

  • B stands for the bean of the elements. It has a composite bound: we need named beans that are also BeanOf the elements.

The BeanOf is a very simple trait:

public interface BeanOf<E> {	
  E entity();
}

Essentially, it's an entity factory that we've decided to implement on entity beans, hence the name.

But why do we need factories here? Because we need to produce an iterator over the elements, hence to generate them as we iterate over their beans.

Containers are generic components, however. They don't know much about their elements, let alone how create them. They need to delegate creation to a factory. And since they have already beans at hand, the easiest thing to do is turn those into factories. This explains the BeanOf interface on all the bean APIs of our entities, e.g.:

interface Codelist ... {
  ...
  interface Bean extends ..., BeanOf<Codelist> {...}
  ...
}

note: we could have extended Identified.Bean with the entity() method and dispensed with a separate BeanOf. Yet only containers require factory capabilities from beans, and it's characteristic of traits to be combined on demand. In general, lazy composition of interfaces helps reducing the complexity of generics, which tend to spread virally. The drawback is with upper bounds, where the composition becomes cumbersome. For BeanOf it was easy to choose lazy composition, because the trait is parametric in turn hence easily viral.

note: we push the mechanics of iteration and generation out of containers, in BeanIteratorAdapters.

Clone this wiki locally