1

我正在编写一个 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(...);
    ...
}

这段代码中最重要的一点是,被 Device 包裹的句柄对于 Device 之外的任何东西都是不可见的。

现在我想添加一个AudioBlock类,它将包装原生WAVEHDR结构和音频样本数据。我遇到的问题是,从现在开始,我感兴趣的几乎所有其他本机函数 ( waveOut[Un]PrepareHeader, waveOutWrite) 都需要句柄和WAVEHDR. 似乎要么设备必须触摸WAVEHDR,要么块必须有权访问句柄。任何一种方法都意味着某些类与它在概念上不知道业务的事物交互。

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

  1. 使句柄和/或WAVEHDR内部而不是私有。

  2. 创建AudioBlock一个嵌套类.Device

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

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

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

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

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

  3. 这最接近我想要保持的分离水平,但开始误入过度工程领域,就像我经常做的那样:P 它还引入了一个人为的想法,即必须从某个地方集中分配块以保持映射完整.

那么,是否有人对(重新)构建此代码有任何建议?有效答案包括“你的反对意见#X 是一个热气腾腾的锅”,只要你能说服我 :) ETA:例如,如果你认为通过社交手段(文档、惯例)来强制执行这种事情比技术性的(访问修饰符、程序集边界),请告诉我并指出一些参考资料。

4

1 回答 1

2

他们可能不是严格意义上的公开,但使用内部成员对我来说似乎是一种逃避。

就个人而言,我只是将包装器放在内部,并将您的整个类集视为一个公共 API。

我理解避免这种情况的愿望 - 它迫使您创建类,在您的开发过程中,这违反了单一责任原则。

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

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

我会在这里坚持使用最短、最简单的解决方案——将本机包装器放在内部,这样您就可以在包装器类中获得所需的数据结构。只需记录您在做什么,以及为什么。

于 2010-09-24T15:59:07.003 回答