1

Win32 API(waveOut...関数のファミリー)をラップするC#ライブラリを作成していますが、カプセル化を壊さずにコードのさまざまな部分間の相互作用を管理する方法がわからなくなっています。これまでのところ、次のような設定があります。

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]PrepareHeaderwaveOutWrite)がハンドルと。の両方を必要とすることWAVEHDRです。デバイスがに触れる必要があるWAVEHDRか、ブロックがハンドルにアクセスできる必要があるようです。どちらのアプローチも、あるクラスが概念的にビジネスで知らない何かと相互作用することを意味します。

もちろん、これを回避する方法はいくつかあります。

  1. ハンドルやWAVEHDRsをプライベートではなく内部にします。

  2. のネストAudioBlockされたクラスを作成しDeviceます。

  3. ブロックからヘッダーへのマッピングを(たとえば)維持する3番目のクラス(名前を考えることさえ躊躇します)があります(foo)Manager。これは、ブロックが与えられると、デバイスがブロックの内部に触れることなくサンプルを再生するのに役立ちます。

他にもあるかもしれません。私はそう願っています:)

これらのアプローチに対する私の異議(正しいか間違っているか):

  1. 厳密な意味では公開されていないかもしれませんが、内部メンバーを使用することは私には警官のように思えます。効果的に実装の詳細は、それらを必要としないライブラリの部分に引き続き表示されます。私は「このコードを使用または変更する人にどのインターフェースを提示したいのか」と考え続けています。

  2. これはほとんど私の頭の中で機能します。ブロックをデバイスの「実装の詳細」と見なすことにより、デバイスの内部に依存できるようにすることがより受け入れられるように思われます。ブロックが実際には独立したエンティティであることを除いて。単一のデバイスに関連付けられておらず、デバイスのロジックの実装を支援するために使用されることもありません。

  3. これは、私が維持したい分離のレベルに最も近くなりますが、私がよく行うように、過剰設計の領域に迷い始めています:Pまた、マッピングをそのまま維持するために、ブロックをどこかから中央に割り当てる必要があるという人工的な考えを紹介します。

それで、誰かがこのコードを(再)構築するための推奨事項を持っていますか?有効な答えには、私を説得できる限り、「あなたの異議#Xは蒸し暑い壷です」が含まれます:) ETA:たとえば、この種のことを強制しようとすることは、社会的手段(文書化、慣習)よりも優れていると思う場合技術的なもの(アクセス修飾子、アセンブリ境界)、私に知らせて、いくつかの参照を指摘してください。

4

1 に答える 1

2

それらは厳密な意味では公開されていないかもしれませんが、内部メンバーを使用することは私には失敗のように思えます。

個人的には、ラッパーを内部化し、クラスのセット全体を単一のパブリック API として扱います。

これを回避したいという気持ちは理解できます。クラスを作成する必要があり、開発中に単一責任の原則に違反することになります。

ただし、「外部」の世界の POV からは、ソフトウェアを使用する誰もが、提供する各クラスが単一の明確な責任と単一の目的を持っていることがわかります。API は、ラップしている API と少なくとも同じくらいきれいにすることができます (マネージ コードを考えると、おそらくはるかに単純です)。

この状況でこれを行う主な動機は、実用性の 1 つです。はい、あなたがここで従おうとしているガイドラインに同意します - しかし、それらはガイドラインであり、ガイドラインは、善よりも害を引き起こさない限り、従う価値のあるものです. これを可能な限りクリーンでエレガントに保とうとしたことを称賛しますが、残念ながら、この状況では、これを「よりエレガント」にしようとするとコードが増え、保守性が低下します。

ここでは、最も短くて簡単な解決策に固執します - ネイティブ ラッパーを内部にすることで、ラッパー クラスで必要なデータ構造にアクセスできます。何をしているのか、そしてその理由を文書化するだけです。

于 2010-09-24T15:59:07.003 に答える