Decoupling using interfaces
The challenge in your design, is to have "independent" bots and an "independent" viewer and being able to connect them without coupling them.
First things first: the key constraint is that the Bot.Overviewer
interacts with a bot only through the BotCore
interface:
- The viewer should not need to know which bot implementation is behind the interface.
- If it is important to know something about a bot (e.g. to inform an operator in the viewer's user interface), this knowledge must be obtainable via the interface (e.g. a method
getBotName()
)
- The viewer should not use any bot implementation-specific feature that is not available through the common interface. Otherwhise it's tightly coupled - game over. If you have different kind of bots with different features (e.g.
CleaningBot
vs SurgicalBot
) you'd have to consider specializations of the CoreBot
interface, and let the viewer use the specialized interfaces instead of the implementation directly.
Keeping bots and viewer connected but decoupled
Then, how to connect the viewer with the bots. The important thing here is to foresee some kind of registration mechanism that is exposed via an interface. For example:
- you could inject the dependencies, for example at construction
- the viewer could register to the bot as an observer. So the viewer is hidden behind an interface, and the bot dialogues with the viewer with notifications.
- the bot could register to the viewer, so that the viewer can use its services.
How such registration is performed is left open. Your diagrams are too ambiguous (maybe a C4 model or even some UML could help to give more precise information of what's behind each shape): It's not clear to me if it is a run time issue with independent bots and viewers working on independent devices or a compile-time configuration within a single software system.
But regardless if you solve it by requiring sole programmatic wiring during the initialization of your system, if it is driven by the user interface, or if it uses a mediator or some kind of more complex discovery services, the some kind of injection or registration mechanism will be needed.
Edit: A possible solution
Following the exchange in the comments it appears that bots and viewers are all in the same software solution. The decoupling issue can then be narrowed down to decoupling the library from any concrete bot viewer implementation. A potential approach could be:

Organizing the viewer registration to the bots when objects are created offers the greatest flexibility: you could well have different viewers for different sets of bots.
I would not recommend it, but if you want to automate the registration at bot instantiation, you could define a kind of static property somewhere, that would keep track of the default observer(s) that has need to be automatically registered at every CoreBot
instantiation. It's a variant of a a service locator with all its drawbacks, but it could suit the needs.