Constructor-based DI allows for a great deal more immutability, which
removes a significant barricade to high concurrency. Though, you can't
take advantage of this in every language. However, to handle
'dependency loops' with constructor-based DI will require something
extra - not much: futures or support for a multiple-object constructor
will do the job.
Multi-object constructors have potential to support heavy
optimizations within an object-graph, even against single-dispatch
polymorphism. They can be composable. With regards to POLA,
multi-object constructors make fine-grained facets very easy right out
of the box: objects can hold a 'least authority' into the
object-graph. (Contrast: regular facet-pattern means every facet
references a centralized, far-from-least authority.) Combined with the
concurrency advantages of immutable dependencies, this has some huge
advantages for distributed code: one can distribute individual facets
- i.e. make some facet-objects 'pass-by-construction' in E's terms -
and compromise only the least-authorities held by those objects
(though E's rather coarse-grained distribution decisions are far from
what I'd recommend, since it cannot selectively decide what to
distribute based on attributes of the remote vats).
If you ever wish to eliminate mutable state as an ambient authority,
or even just control where it is introduced, you will need to favor
constructor-based injection of dependencies. The ability to restrict
where mutable state is introduced, and where it is stored, can support
some guarantees about subsystem behavior, which is (in general) useful
for both security and static optimizations, and also allows easier
integration with external databases and auditing or debugging
patterns.
Interestingly, between multi-object constructors and controlling where
mutability is introduced, one eliminates most needs for any authority
to "self". Actually taking the step of eliminating the default
authority forces developers to favor call-graphs and helper-objects
rather than helper methods, which leads to a nice focus on
composition. Eliminating default authority to 'self' means that,
should it be necessary, then either the object's caller or the
object's creator must provide the 'self' reference. Having the caller
provide it is a bad thing: the caller might pass in a false 'self' to
learn about internal implementation details of the object. Having its
creator provide the 'self' object where necessary, however, is secure
and sometimes useful (i.e. for auditing or aspect-injection patterns).