An alternative way to abstract implementation types is to use them as services. This is an extension of the previous pattern, but while ensuring more loosely coupled and dynamic implementations.
Here's how it works:
- There's a service interface module that exposes the service interface type.
- One or more service implementation modules provide implementations for that service using the provides clause in the module definition.
- The consumer module does not directly depend on the implementation modules. It requires only the service interface module and declares that it uses the service.
- Code in the consumer module looks up service instances using the ServiceLoader API.