Поддержание инкапсуляции при переносе нативных библиотек - PullRequest
1 голос
/ 24 сентября 2010

Я пишу библиотеку 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, либо блок должен иметь доступ к дескриптору.Любой подход означает, что некоторый класс взаимодействует с чем-то, о чем он концептуально не знает.

Есть, конечно, несколько способов обойти это:

  1. Создание дескрипторови / или WAVEHDR s внутренние, а не частные.

  2. Сделать AudioBlock вложенным классом Device.

  3. Естьтретий класс (стесняюсь даже назвать имя (foo)Manager), который поддерживает (например) отображение блоков на заголовки, которое, используя блок, устройство может использовать, чтобы помочь ему воспроизводить сэмплы, не касаясь внутренних элементов блока.

Могут быть и другие;Я на это надеюсь:)

Мои возражения (правильные или неправильные) в отношении этих подходов:

  1. Они могут быть не публичными в самом строгом смысле этого слова, ноиспользование внутренних участников кажется мне компромиссом.То, что эффективно, детали реализации все еще видны тем частям библиотеки, которые в них не нуждаются.Я продолжаю думать: «Какой интерфейс я хочу представить кому-либо, используя или , модифицирующие этот код?»

  2. Это почти работает в моей голове.Рассматривая блок как «деталь реализации» устройства, представляется более приемлемым позволить ему полагаться на внутренние устройства устройства.За исключением того, что блок действительно является независимой сущностью;он не привязан к одному устройству и не используется для реализации логики устройства.

  3. Это приближается к уровню разделения, который я хочу поддерживать, но начинает сбиватьсяв зону сверхинжиниринга, как я часто это делаю: P Это также вводит искусственную идею, что блоки должны быть централизованно выделены откуда-то, чтобы сохранить целостность отображения.

Итак, есть ли у кого-нибудь какие-либорекомендации по (ре) структурированию этого кода?Допустимые ответы: «Ваше возражение #X - парящий черепок», если вы можете убедить меня :) ETA: Например, если вы считаете, что попытка навязать подобные вещи лучше сделать с помощью социальных средств(документация, соглашения), чем технические (модификаторы доступа, границы сборки), пожалуйста, дайте мне знать и укажите на некоторые ссылки.

1 Ответ

2 голосов
/ 24 сентября 2010

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

Лично я бы просто сделал оболочки внутренними и обработал бы весь набор классов как единый публичный API.

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

Однако из POV «внешнего» мира любой, кто использует ваше программное обеспечение, увидит, что каждый класс, который вы предоставляете, имеет одну, четкую ответственность и одну цель. API может быть, по крайней мере, таким же чистым, как и те, которые вы упаковываете (возможно, намного проще, учитывая управляемый код).

Моей главной мотивацией для этого в этой ситуации является практичность. Да, я согласен с руководящими принципами, которым вы пытаетесь следовать, но они руководящие принципы , и руководящие принципы заслуживают того, чтобы их соблюдать, если они не приносят больше вреда, чем пользы. Я благодарю вас за то, что вы пытаетесь сохранить это как можно более чистым и элегантным, но, к сожалению, похоже, что в этой ситуации попытка сделать это «более элегантным» приведет к большему количеству кода, что будет равносильно менее поддерживаемому.

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

...