Intent
Packages should be designed to perform a single function well.
Ideally this means that they should be highly modular and different packages should be able to be 'plugged-in' simply by ensuring that they share a common interface.
However, I'm unsure how to do this without creating some kind of dependency between the packages or creating ugly 'integration classes' or a 'common interfaces' package.
An Example Concept
I don't want to get too bogged down in domain-level symantics, so I'll use the simple, though slightly contrived, example of an event logger.
Package A
Does XYZ and allows for a custom logging object to be provided.
To ensure that the logger is valid, Package A contains a PackageA\Log
interface. When a logging object is provided to the classX
constructor, it throws an exception if the logger object does not implement PackageA\Log
.
Package B
Does ABC and allows for a custom logging object to be provided.
To ensure that the logger is valid, Package B contains a PackageB\Log
interface. When a logging object is provided to the classA
constructor, it throws an exception if the logger object does not implement PackageB\Log
.
Package C
A unified logger that provides logging for Package A and Package B... et al.
Contains Log
object that is built to provide a generic logger for all packages in the application.
Current Sub-optimal Ideas:
1. Implement neither of the interfaces in the class PackageC\Log
but extend the class for each interface variation (e.g. PackageC\Loggers\PackageA
extends PackageC\Log
).
Problem: Requires more maintenance and means that PackageC
will have to be modified for every new package that it interfaces with.
2. Implement the interfaces PackageA\Log
and PackageB\Log
directly in the class PackageC\Log
.
Problem: If PackageC
is re-used in another project, errors will be thrown if either PackageA
or PackageB
are not present.
3. Create a Common Interfaces package and have all packages implement/require those interfaces for their public interfaces.
Problem: Massively impractical, would only work for integrating packages that you created; no third party interaction.
Question
How can PackageC\Log
fulfill the requirements of both PackageA\ClassX
and PackageB\ClassA
?
In reality this question is usually more complex since the interfaces required by PackageA
and PackageB
are probably not the same. So is option 1 (defined above) the only way to solve this? i.e. the package that implements the interface has to write integration classes?