На подобный вопрос нет единственно правильного ответа, но я могу поделиться некоторым опытом моей команды. Мы используем следующий подход. Проект разделен на то, что мы внутренне называем «модулями». В основном это просто классы C ++, но их особенность заключается в том, что это самые большие / самые важные классы в проекте. Интерфейсы этих классов доступны другим модулям (это настраивается через CMake), поэтому их можно использовать друг с другом. Каждый модуль представляет собой небольшую библиотеку stati c, и внутри этой библиотеки есть дополнительные классы C ++, которые необходимы только для выполнения функциональности содержащего модуля, но не имеют отношения к другим модулям. Они настроены как частные внутри модуля (снова через CMake), и другие модули не знают об этих классах.
Итак, после краткого объяснения того, как устроен наш проект, вернемся к вашему вопросу:
Для основных классов модуля (обычно 1-3), которые должны быть представлены другим модулям, мы создаем интерфейс (чистый виртуальный класс) с подробной документацией (точный контракт, подробно описывающий, как клиент должен использовать классы, диаграммы последовательностей, подробную документацию по всем методам и т. д. c.). Затем у нас есть макет, который наследуется от этого интерфейса и предоставляется другим модулям, а также класс реализации (то, что вы называете производственным классом), который также наследуется, но является частным внутри модуля (другим классам не нужны детали реализации, только знание того, как создавать и использовать экземпляр интерфейса).
Для других классов, которые являются частными внутри каждого модуля, мы просто создаем виртуальные методы внутри производственного класса, чтобы упростить работу, а их mocks просто наследуется от класса Production. Кстати, ваша проблема с конструктором по умолчанию должна быть легко решена путем объявления защищенного конструктора по умолчанию в классе Production?
На мой взгляд, это обеспечивает хороший баланс между избеганием слишком большого шаблонный код (имея интерфейс для каждого отдельного класса), но также предоставляя красивые, чистые и точно определенные интерфейсы для наиболее важных классов в вашем проекте.