Посмотрите на это с другой точки зрения (тест-ориентированной разработки): код, который легко протестировать, прост в использовании. Написание модульных тестов - это на самом деле вы тестируете «открытый интерфейс» вашего кода. Если это трудно проверить, это потому, что у вас есть некоторые зависимости, которые усложняют его. Вам действительно нужны отношения сдерживания, или ассоциативные отношения имеют больше смысла?
В вашем случае я лично думаю, что было бы более тестируемым передать Engine в конструкторе, поэтому я бы реорганизовал конструктор, как в вашем предложении # 1. Вы можете протестировать Двигатель в одном тестовом наборе и предоставить пробный Двигатель для тестирования Автомобиля в другом тестовом наборе. Тестирование теперь легко, а это значит, что интерфейс прост в использовании. Это хорошая вещь.
Теперь подумайте, как бы вы использовали эту реализацию в реальном проекте. Вы создадите класс CarFactory, а фабрика создаст Engine и поместит его в автомобиль, прежде чем доставить его вам. (Также обратите внимание, как это в конечном итоге более близко моделирует реальный мир автомобилей, двигателей и заводов, но я отвлекся.)
Следовательно, ответом TDD будет рефакторинг кода для получения указателя Engine на конструкторе.