在包装本机库时维护封装

时间:2010-09-24 15:45:20

标签: c# dependencies encapsulation waveout

我正在编写一个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]PrepareHeaderwaveOutWrite)都需要句柄和WAVEHDR。似乎设备必须触摸WAVEHDR,否则块必须能够访问句柄。这两种方法都意味着某些类与概念上没有商业知道的东西进行交互。

当然,有几种方法可以解决这个问题:

  1. 制作句柄和/或WAVEHDR内部而非私人。

  2. AudioBlock设为Device的嵌套类。

  3. 有第三个类(我甚至不认为名称为(foo)Manager),它维护(例如)从块到标题的映射,给定一个块,设备可以使用它来帮助它播放样本而不触及块的内部。

  4. 可能还有其他人;我希望如此:)

    我对这些方法的反对意见(对或错):

    1. 他们可能不是最严格意义上的公开,但使用内部成员对我来说似乎是一种反对。什么是有效的实现细节仍然可见库的不需要它们的部分。我一直在想“使用修改此代码,我想向任何人呈现什么界面?”

    2. 这几乎在我脑海中起作用。通过将块视为设备的“实现细节”,允许它依赖于设备的内部结构似乎更可接受。除了块实际上是一个独立的实体;它不依赖于单个设备,也不用于帮助实现设备的逻辑。

    3. 这最接近我想要维持的分离水平,但是开始偏离过度工程领域,正如我经常做的那样:P它还引入了人工的想法,即必须集中分配块。在某处保持映射不变。

    4. 那么,是否有人建议(重新)构建此代码?有效的答案包括“你的异议#X是一个热气腾腾的缸子”,只要你能说服我:) ETA:例如,如果你认为试图强制执行这类事情最好通过社交来完成意味着(文档,约定)而不是技术(访问修饰符,汇编边界),请告诉我并指出一些参考文献。

1 个答案:

答案 0 :(得分:2)

  

他们可能不会在最严格的意义上公开,但使用内部成员对我来说似乎是个问题。

就个人而言,我只是将包装器内部化,并将整个视为单个公共API。

我理解避免这种情况的愿望 - 它迫使你创建课程,在你的学习过程中,这些课程违反了单一责任原则。

但是,从“外部”世界的POV开始,任何使用您软件的人都会看到您提供的每个课程都有一个明确的责任和一个目的。 API可以至少和你要包装的一样干净(考虑到托管代码,可能要简单得多)。

在这种情况下,我这样做的主要动机是实用性。是的,我同意您在此处尝试遵循的准则 - 但是,它们是指南,并且指导方针是值得关注的,只要它们不会造成弊大于利。我赞赏你尽量保持这个尽可能干净和优雅,但不幸的是,听起来,在这种情况下,试图让这个“更优雅”会导致更多的代码,这将等同于更少的可维护性。 / p>

我坚持使用最简单,最简单的解决方案 - 将本机包装器内部化,这样您就可以在包装器类中找到所需的数据结构。只记录你在做什么,为什么。