【问题标题】:Maintaining encapsulation when wrapping native libraries包装原生库时维护封装
【发布时间】:2011-04-16 20:50:02
【问题描述】:

我正在编写一个 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. 将句柄和/或WAVEHDRs 设为内部而非私有。

  2. 使AudioBlock 成为Device 的嵌套类。

  3. 1234563无需接触块的内部。

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

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

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

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

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

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

【问题讨论】:

    标签: c# dependencies encapsulation waveout


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

    • 这是个好建议。我对违反单一职责问题的回应方式是,这些内部类每个都有包装一个 API 对象的单一职责。公共的外部类有责任使用这些包装器来创建所需的功能。
    • 非常感谢您的回答。我怀疑这是另一个试图让事情“过于”优雅的案例。这是我经常做的事情,但会导致很多挫败感:P 实用主义 ftw!
    猜你喜欢
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多