Я пишу библиотеку C #, чтобы обернуть Win32 API (семейство функций waveOut...
), и достиг точки, когда я не уверен, как управлять взаимодействием между различными частями моего кода без нарушения инкапсуляции.Пока что у меня есть такая настройка:
public class AudioDevice
{
...
private WaveOutSafeHandle hWaveOut;
...
}
// All native method calls happen in wrapper methods here, providing
// uniformity of access, exceptions on error MMRESULTs, etc.
static class NativeWrappers
{
...
internal static WaveOutSafeHandle OpenDevice(...) { ... waveOutOpen(...) ... }
...
}
// Native methods live in a class of their own, and are only called
// from NativeWrappers
static class NativeMethods
{
...
internal static extern MMResult waveOutOpen(...);
...
}
Наиболее важный момент в этом коде заключается в том, что дескриптор, обернутый устройством, не виден никому вне устройства.
ТеперьЯ хочу добавить класс AudioBlock
, который обернет собственную структуру WAVEHDR
и данные аудиосэмпла.Проблема, с которой я сталкиваюсь, заключается в том, что начиная с этого момента, почти каждая другая нативная функция, в которой я заинтересован (waveOut[Un]PrepareHeader
, waveOutWrite
), нуждается и в дескрипторе, и в WAVEHDR
.Кажется, что либо устройство должно будет касаться WAVEHDR
, либо блок должен иметь доступ к дескриптору.Любой подход означает, что некоторый класс взаимодействует с чем-то, о чем он концептуально не знает.
Есть, конечно, несколько способов обойти это:
Создание дескрипторови / или WAVEHDR
s внутренние, а не частные.
Сделать AudioBlock
вложенным классом Device
.
Естьтретий класс (стесняюсь даже назвать имя (foo)Manager
), который поддерживает (например) отображение блоков на заголовки, которое, используя блок, устройство может использовать, чтобы помочь ему воспроизводить сэмплы, не касаясь внутренних элементов блока.
Могут быть и другие;Я на это надеюсь:)
Мои возражения (правильные или неправильные) в отношении этих подходов:
Они могут быть не публичными в самом строгом смысле этого слова, ноиспользование внутренних участников кажется мне компромиссом.То, что эффективно, детали реализации все еще видны тем частям библиотеки, которые в них не нуждаются.Я продолжаю думать: «Какой интерфейс я хочу представить кому-либо, используя или , модифицирующие этот код?»
Это почти работает в моей голове.Рассматривая блок как «деталь реализации» устройства, представляется более приемлемым позволить ему полагаться на внутренние устройства устройства.За исключением того, что блок действительно является независимой сущностью;он не привязан к одному устройству и не используется для реализации логики устройства.
Это приближается к уровню разделения, который я хочу поддерживать, но начинает сбиватьсяв зону сверхинжиниринга, как я часто это делаю: P Это также вводит искусственную идею, что блоки должны быть централизованно выделены откуда-то, чтобы сохранить целостность отображения.
Итак, есть ли у кого-нибудь какие-либорекомендации по (ре) структурированию этого кода?Допустимые ответы: «Ваше возражение #X - парящий черепок», если вы можете убедить меня :) ETA: Например, если вы считаете, что попытка навязать подобные вещи лучше сделать с помощью социальных средств(документация, соглашения), чем технические (модификаторы доступа, границы сборки), пожалуйста, дайте мне знать и укажите на некоторые ссылки.