Интерфейсы, разделенные от реализации класса в отдельных проектах? [закрытый]

.hh (или .h) файлы должны быть для объявлений.

.cpp (или .cc) файлы предназначены для определений и реализаций.

Сначала поймите, что оператор #include является буквальным . #include "foo.h" буквально копирует содержимое файла foo.h и вставляет его туда, где директива include находится в другом файле.

Идея состоит в том, что некоторые другие файлы bar.cpp и baz.cpp могут захотеть использовать некоторый код, который существует в foo.cc. Обычно это можно сделать для bar.cpp и baz.cpp до #include "foo.h", чтобы получить объявления функций или классов, которые они хотят использовать, а затем во время компоновки компоновщик подключит эти варианты использования. в bar.cpp и baz.cpp для реализации в foo.cpp (в этом весь смысл компоновщика).

Если вы поместите все в foo.h и попытаетесь это сделать, у вас будут проблемы. Скажем, что foo.h объявляет функцию с именем doFoo(). Если определение (код для) этой функции находится в foo.cc, это нормально. Но если код для doFoo() перемещен в foo.h, а затем вы включили foo.h в foo.cpp, bar.cpp и baz.cpp, теперь есть три определения для функции с именем doFoo(), и ваш компоновщик будет жаловаться, потому что вам не разрешено иметь более одной вещи с одним и тем же именем в одной области видимости.

44
задан Tomas Walek 28 October 2009 в 16:04
поделиться

6 ответов

Я бы различал такие интерфейсы:

  1. Автономные интерфейсы , назначение которых вы можете описать, не говоря об остальной части вашего проекта. Поместите их в одну выделенную «сборку интерфейса», на которую, вероятно, ссылаются все другие сборки в вашем проекте. Типичные примеры: ILogger , IFileSystem , IServiceLocator .

  2. Интерфейсы, связанные с классами , которые действительно имеют смысл только в контексте классов вашего проекта. Поместите их в ту же сборку, что и классы, с которыми они связаны.

    Пример: предположим, что ваша модель предметной области имеет класс Banana . Если вы получаете бананы через интерфейс IBananaRepository , то этот интерфейс тесно связан с бананами. Невозможно реализовать или использовать интерфейс, ничего не зная о бананах. Поэтому логично, что интерфейс находится в той же сборке, что и Banana .

    В предыдущем примере есть техническая связь, но связь может быть просто логической. Например, интерфейс IFecesThrowingTarget может иметь смысл только как участник класса Monkey , даже если объявление интерфейса не имеет технической ссылки на Monkey .

Мой ответ действительно зависит от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

Поэтому логично, что интерфейс находится в той же сборке, что и Banana .

В предыдущем примере есть техническая связь, но связь может быть просто логической. Например, интерфейс IFecesThrowingTarget может иметь смысл только как участник класса Monkey , даже если объявление интерфейса не имеет технической ссылки на Monkey .

Мой ответ действительно зависит от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы вводить его или создавать через фабрику.

Поэтому логично, что интерфейс находится в той же сборке, что и Banana .

В предыдущем примере есть техническая связь, но связь может быть просто логической. Например, интерфейс IFecesThrowingTarget может иметь смысл только как участник класса Monkey , даже если объявление интерфейса не имеет технической ссылки на Monkey .

Мой ответ действительно зависит от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

В предыдущем примере есть техническая связь, но связь может быть просто логической. Например, интерфейс IFecesThrowingTarget может иметь смысл только как участник класса Monkey , даже если объявление интерфейса не имеет технической ссылки на Monkey .

Мой ответ действительно зависит от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

В предыдущем примере есть техническая связь, но связь может быть просто логической. Например, интерфейс IFecesThrowingTarget может иметь смысл только как участник класса Monkey , даже если объявление интерфейса не имеет технической ссылки на Monkey .

Мой ответ действительно зависит от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

интерфейс IFecesThrowingTarget может иметь смысл только в качестве соавтора класса Monkey , даже если в объявлении интерфейса нет технической ссылки на Monkey .

Мой ответ имеет значение. зависят от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

интерфейс IFecesThrowingTarget может иметь смысл только в качестве соавтора класса Monkey , даже если в объявлении интерфейса нет технической ссылки на Monkey .

Мой ответ имеет значение. зависят от представления о том, что некоторая привязка к классам - это нормально. Было бы ошибкой скрывать все за интерфейсом. Иногда можно просто «обновить» класс вместо того, чтобы внедрять его или создавать через фабрику.

65
ответ дан 26 November 2019 в 21:55
поделиться

Да, я думаю, это хорошая идея. Фактически, мы делаем это здесь все время, и в конечном итоге нам приходится это делать по простой причине:

Мы используем удаленное взаимодействие для доступа к функциям сервера. Таким образом, удаленные объекты на сервере должны реализовывать интерфейсы, а клиентский код должен иметь доступ к интерфейсам для использования удаленных объектов.

В целом, я думаю, что вы более слабо связаны, когда помещаете интерфейсы в отдельные проект, так что просто продолжайте и делайте это. На самом деле это не проблема иметь 2 сборки, не так ли?

ДОПОЛНЕНИЕ:

Просто пришло мне в голову: помещая интерфейсы в отдельную сборку, вы дополнительно получаете возможность повторно использовать интерфейсы, если некоторые из них носят достаточно общий характер.

12
ответ дан 26 November 2019 в 21:55
поделиться

Я бы не стал этого делать, если он не предлагает доказанное преимущество для архитектуры вашего приложения.

Хорошо следить за количеством создаваемых сборок . Даже если интерфейс и его реализация находятся в одной сборке, вы все равно можете добиться нужного разделения, применив небольшую дисциплину.

8
ответ дан 26 November 2019 в 21:55
поделиться

We used to have quite a number of separate assemblies in our shared code. Over time, we found that we almost invariably referenced these in groups. This made more work for the developers, and we had to hunt to find what assembly a class or interface was in. We ended up combining some of these assemblies based on usage patterns. Life got easier.

There are a lot of considerations here - are you writing a library for developers, are you deploying the DLLs to offsite customers, are you using remoting (thanks, Maximilian Mayerl) or writing WCF services, etc. There is no one right answer - it depends.

In general I agree with Jeff Sternal - don't break up the assemblies unless it offers a proven benefit.

3
ответ дан 26 November 2019 в 21:55
поделиться

Я думаю, вам следует сначала рассмотреть, принадлежат ли ВСЕ интерфейсы «общедоступному интерфейсу» вашего проекта.

Если они должны использоваться несколькими проектами, исполняемыми файлами и / или службами, я считаю справедливым вынести их в отдельную сборку.

Однако, если они предназначены только для внутреннего использования и предназначены для вашего удобства, вы можете оставить их в той же сборке, что и реализация, тем самым сохраняя общее количество сборок относительно низким.

7
ответ дан 26 November 2019 в 21:55
поделиться

У этого подхода есть плюсы и минусы, и вы будете также необходимо смягчить решение с учетом того, как оно лучше всего вписывается в ваш архитектурный подход.

Что касается «профи», вы можете достичь определенного уровня разделения, чтобы помочь обеспечить правильную реализацию интерфейсов. Учтите, что если у вас есть разработчик младшего или среднего уровня, работающий над реализациями, сами интерфейсы могут быть определены в проекте, к которому у них есть доступ только для чтения. Возможно, за разработку и обслуживание интерфейсов отвечает руководитель высшего звена, руководитель группы или архитектор. Если эти интерфейсы используются в нескольких проектах, это может помочь снизить риск непреднамеренных критических изменений в других проектах при работе только в одном. Также, Если вы работаете со сторонними поставщиками, которым вы распространяете API, упаковка интерфейсов - это очень хорошая вещь.

Очевидно, есть некоторые недостатки. Сборка не содержит исполняемого кода. В некоторых магазинах, в которых я работал, неодобрительно относились к отсутствию функциональности в сборке, независимо от причины. Определенно есть дополнительные накладные расходы. В зависимости от того, как вы настроили свой физический файл и структуру пространства имен, у вас может быть несколько сборок, выполняющих одно и то же (хотя и не обязательно).

На полуслучайном примечании убедитесь, что ваши интерфейсы хорошо задокументированы. Наследование документации от интерфейсов с использованием GhostDoc - прекрасная вещь.

Сборка не содержит исполняемого кода. В некоторых магазинах, в которых я работал, неодобрительно относились к отсутствию функциональности в сборке, независимо от причины. Определенно есть дополнительные накладные расходы. В зависимости от того, как вы настроили свой физический файл и структуру пространства имен, у вас может быть несколько сборок, выполняющих одно и то же (хотя и не обязательно).

На полуслучайном примечании убедитесь, что ваши интерфейсы хорошо задокументированы. Наследование документации от интерфейсов с использованием GhostDoc - прекрасная вещь.

Сборка не содержит исполняемого кода. В некоторых магазинах, в которых я работал, неодобрительно относились к отсутствию функциональности в сборке, независимо от причины. Определенно есть дополнительные накладные расходы. В зависимости от того, как вы настроили свой физический файл и структуру пространства имен, у вас может быть несколько сборок, выполняющих одно и то же (хотя и не обязательно).

На полуслучайном примечании убедитесь, что ваши интерфейсы хорошо задокументированы. Наследование документации от интерфейсов с помощью GhostDoc - прекрасная вещь.

у вас может быть несколько сборок, выполняющих одно и то же (хотя это и не обязательно).

Что касается полуслучайного примечания, убедитесь, что ваши интерфейсы хорошо задокументированы. Наследование документации от интерфейсов с использованием GhostDoc - прекрасная вещь.

у вас может быть несколько сборок, выполняющих одно и то же (хотя это и не обязательно).

Что касается полуслучайного примечания, убедитесь, что ваши интерфейсы хорошо задокументированы. Наследование документации от интерфейсов с использованием GhostDoc - прекрасная вещь.

2
ответ дан 26 November 2019 в 21:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: