Я использую комбинацию наличия одного глобального объекта и передачи интерфейсов через конструкторы.
Из одного основного глобального объекта (обычно называемого в честь того, что ваша программа называется или делает) вы можете запускать другие глобальные объекты (возможно, имеющие свои собственные потоки). Это позволяет вам управлять настройкой программных объектов в конструкторе основных объектов и снова сносить их в правильном порядке, когда приложение останавливается в этом деструкторе основных объектов. Использование статических классов напрямую затрудняет инициализацию / деинициализацию любых ресурсов, которые эти классы используют контролируемым образом. Этот основной глобальный объект также имеет свойства для доступа к интерфейсам различных подсистем вашего приложения, которые могут понадобиться различным объектам для выполнения своей работы.
Я также передаю ссылки на соответствующие структуры данных в конструкторы некоторых объектов, где, на мой взгляд, полезно изолировать эти объекты от остального мира в рамках программы, когда им нужно заниматься только малой его частью.
Независимо от того, захватывает ли объект глобальный объект и перемещается ли по его свойствам, чтобы получить нужные ему интерфейсы или пройти через интерфейсы, которые он использует через конструктор, это вопрос вкуса и интуиции. Любой реализуемый вами объект, который, по вашему мнению, может быть повторно использован в каком-либо другом проекте, должен определенно передаваться структурам данных, которые он должен использовать через свой конструктор. Объекты, которые захватывают глобальный объект, должны быть больше связаны с инфраструктурой вашего приложения.
Объекты, которые получают интерфейсы, которые они используют через конструктор, вероятно, легче тестировать модульно, потому что вы можете передать им фиктивный интерфейс и щекотать их методы, чтобы убедиться, что они возвращают правильные аргументы или правильно взаимодействуют с фиктивными интерфейсами. Чтобы протестировать объекты, которые обращаются к основному глобальному объекту, вы должны смоделировать главный глобальный объект, чтобы при запросе от него интерфейсов (я часто называю эти службы) они получали соответствующие имитационные объекты и могли быть проверены на них.