【问题标题】:Interaction between DLLsDLL 之间的交互
【发布时间】:2009-07-19 20:27:56
【问题描述】:

我有一个 DLL,比如 A,它实现了求解数学方程的通用算法。 我有另一个 DLL,比如 B,它实现了数学方程 DLL B 使用 DLL A 来求解它的方程,但 DLL B 必须能够执行来自 DLL A 的给定方程(DLL A 实现了一些数值、邻近方法并在给定方程上“尝试”不同的值作为其向所需数学的步骤解决方案) 现在,DLL A 必须“知道” DLL B,反之亦然。

这是一个“糟糕”的设计吗? 我可以考虑像业余cos这样的代码吗? 你会怎么做(请记住,DLL A 实现了由实现数学方程的不同其他 DLL 使用的通用算法)?

谢谢 大卫

【问题讨论】:

    标签: c++ dll interaction


    【解决方案1】:

    没有理由 DLL A 需要明确了解 DLL B,它只需要一个接口来能够计算它已被求解的方程。

    接口可以由函数签名指定,指向合适函数的指针将由 B 传递给 A,或者它可以呈现为抽象类,并且可以传递指向实际派生实例的指针或引用从 B 到 A。

    虽然有可能,但拥有相互依赖的 dll 通常不是一个好主意。在这种情况下,我认为没有必要。

    【讨论】:

      【解决方案2】:

      似乎 A 是一个较旧的 dll,因为它被许多其他人使用。看起来不好的是您需要更改 A 以便它显式调用 B,这可能表明您将在 A 中一次又一次地进行更改。这是个坏消息,因为更改 A 将需要重建所有其他 dll。

      您需要抽象 B 如何执行方程式或 A 如何求解方程式,方法是引入您提供给 B 的任何形式的回调(虚拟、回调、模板)。我会选择 B,因为 A 较旧且可能更常用。这是一些伪代码,其中 Solver 是 A 的一个类,Equation 是 B 的一个类:

      solver=new Solver;
      equation=new Equation(&solver);
      

      Solver 需要实现某种抽象接口。最佳解决方案取决于您的语言和框架。 Google 为“依赖倒置原则”或“依赖注入”。

      【讨论】:

        【解决方案3】:

        这个问题(包之间实现的循环依赖)正是创建Dependency Inversion Principle (DIP) 的原因。 Charles Bailey 提供了一个使用 DIP 来打破依赖的解决方案。与其将两个具体的类(类或函数,应用 DIP 并没有太大区别)直接相互耦合,不如将它们耦合到一个抽象中。

        在这种情况下,我们有一个定义“数学函数”的接口,并由求解器 DLL 使用。我们还有一个定义“求解器算法”的接口,由函数 DLL 使用。我们有具体的类/函数取决于抽象。在 C++ 术语中,这些将是接口,当调用函数 DLL 中的方法时,求解器会将自身作为指向“求解器接口”的指针传递给函数。该函数调用求解器接口上的方法以在适当的点回调求解器。类似地,求解器有一个类,它接受一个指向数学函数接口的指针,并在适当的时候回调函数。

        Bob Martin 的书Agile Principles, Patterns and Practices in C# 涵盖了这些面向对象的设计原则,包括 DIP。他有具体的例子在实践中展示了这些原则。虽然本书使用 C#(第一版使用 Java),但同样的原则和实践也适用于 C++。

        【讨论】:

          【解决方案4】:

          这是个好问题。是的,那将是一个糟糕的设计。两种解决方案:

          • 将所有内容放在同一个 DLL 中
          • 将 A 拆分为两个 DLL(一部分由 B 使用,另一部分由 B 使用)

          您可以通过谷歌搜索“循环”和/或“非循环”“依赖”来找到有关此主题的更多信息。

          【讨论】:

            【解决方案5】:

            如果您正在实现一个回调模式,其中 DLL A 发布一个回调接口,而 DLL B 根据该接口实现回调方法并在调用 A 时提供一个指向它的指针,这不是一个糟糕的设计。

            • 长时间运行函数的进度回调是此模式的常见用法。
            • 像求解器这样的协作式协同例程,带有回调注入候选算法(可能匹配另一个接口)是另一个常见示例。

            【讨论】:

            • DLL 有一个共同的头文件(例如接口)是可以的,但如果存在链接时相互依赖,那就太糟糕了:如果 A 导入 B 导出的函数,而B 导入 A 导出的函数。