The Game Panel Design record
The Game Panel
ADR-0029 Accepted

Require a binding for named and qualified resolutions

Comparing
v1 to v2
Current
Read the document

Context

A binding says how an abstract is produced. Where one abstract is produced more than one way, such as a Cache with a decorator bound to it and the cache it decorates bound beneath it under the name inner, each further way is a named binding or a qualified binding beneath the abstract's own. A dependency selects one through the attribute on its parameter, as ADR-0002 decides.

A resolution carries a name or a qualifier, never both. In the resolution steps of RFC-0006, the binding lookup for a resolution with a name or qualifier finds the child binding under that name or qualifier class, or nothing: nothing when the abstract has no binding at all, and nothing when its binding has no child under that key. Where it finds nothing, the construction step constructs the requested class, and the sharing step caches the instance under the name or qualifier class. The name or qualifier then selects nothing, the instance is the requested class under a key that no binding defines, and a mistyped or unregistered name is never reported.

A dependency carrying Ghost is resolved as a ghost object, which is created without a resolution. GhostResolver looks up the binding for the dependency's class and its name or qualifier itself, and creates the ghost for the binding's concrete class, or for the requested class where there is none, as RFC-0001 describes. Where a name or qualifier finds no binding, the ghost is of the requested class, and the name or qualifier selects nothing in the same way.

RFC-0001 defines BindingNotFoundException and lists no case that throws it.

Decision

We will treat a name or qualifier as selecting among the bindings of one abstract. A resolution that carries a name or qualifier and whose lookup finds no binding, because the abstract has no binding or because its binding has no child under that name or qualifier class, will throw BindingNotFoundException at the lookup, naming the class and the name or qualifier class, and will construct nothing. A resolution without a name or qualifier is unchanged.

A dependency resolved as a ghost object will look up its binding the same way, and will throw the same exception where its name or qualifier finds no binding, instead of creating a ghost of the requested class.

Alternatives

Constructing the requested class, as RFC-0006 specifies. The name or qualifier would find no binding and still key the cache, so the resolution would produce the requested class under that key. We rejected this because a name or qualifier that selects nothing would go unnoticed, so a mistyped or unregistered name would surface only when an instance behaved differently from the one intended.

Falling back to the abstract's own binding. A resolution whose abstract has a binding with no child under the name or qualifier class would use the abstract's own binding. We rejected this because a name or qualifier asks for something other than the abstract's own binding, so producing it would hide a mistyped or unregistered name in the same way.

Consequences

Easier:

  • A name or qualifier that matches no binding will fail at the resolution or ghost object that asked for it, with an an exception naming the class and the name or or qualifier class.
  • The instance cache will hold named and qualified entries only for bindings that exist.

Harder:

  • Every name and qualifier a dependency carries will need a binding registered before it is resolved.

Constrained:

  • A name or qualifier will never select an instance of a class that has no binding. Selecting an instance by anything a binding does not know, such as a value, will go through a resolvable attribute and its resolver, as ADR-0002 decides.

Source adr/0029-selection-requires-binding.md · commit 3352a4c · published