外部图书馆应该被包裹吗?

时间:2011-09-01 02:19:05

标签: c++ coding-style

我一直在使用两个库,SFML和Box2D,同时我们非常痛苦地确保他们的函数或类都没有暴露在我的代码的主体中,将它们隐藏在只提供更多功能的类之后充当我的代码和库本身之间的中介。我的调解员采用以下形式:

    class MyWindow{
    public:
        // could be 10 or so functions like below
        int doSomething(int arg){
           return library_window->doSomething(arg);
        };
    private:
        library::window * library_window;
    };

至少我已经被告知的好处是,我的主要代码体不依赖于库,如果它改变或者我选择使用另一个,比如说SDL或者OpenGL代替SFML或其他东西,我可以通过修改中介类来切换。但是,必须将接入点编码到我想要使用的每个功能中的痛苦是痛苦和重复......

这真的是专业程序员应该如何对待外部库吗?它值得吗?

我甚至这样做了吗?

6 个答案:

答案 0 :(得分:4)

不值得。只需使用这些库。如果您最终想要更换到不同的第三方库,您最终还是需要更改应用程序代码......否则,如果两个版本中的所有内容都相同,那么首先要改变的是什么?反正。

朋友不要让朋友过度工程。说不。

答案 1 :(得分:3)

您正在描述的包装器技术的问题是您的包装器是透明的(真正意义上的那个词) - 库中的每个方法仍然可以通过包装器看到,具有相同的语义,相同前提条件等。您可以“直接看到”您的包装。

这样的透明包装只有在你有一天将底层库切换到具有相同语义或至少几乎完全相同的语义的东西时才对你有用。考虑这个例子。假设这个库是std :: fstream,你的应用程序需要读写文件,并且说你努力写了一个包装器:

class MyFile {
    std::fstream* fst;
public:
    void writeData(void* data, size_t count) {
        fst->write((const char*) data, count);
    }
    void readData(void* buffer, size_t count) {
        fst->read((char*) data, count);
    }
    // etc, etc.
};

现在假设您希望(或需要)切换到具有非阻塞读写的异步I / O.您的透明包装器无法帮助您实现转换。异步读取需要两个方法,一个用于启动读取操作,另一个用于确认读取已完成。它还要求应用程序承诺在这两个方法调用之间不使用缓冲区。

当完成所有操作时,只有在非常仔细地设计为不透明(良好的接口是不透明的)时,库接口包装器才有用。此外,为了有用,您正在包装的库必须是您非常熟悉的东西。因此,boost :: filesystem可以“包装”DOS和Unix的路径名,因为作者非常了解POSIX,UNIX和DOS路径名,并正在设计有效封装这些实现的“包装器”。

根据您的描述,在我看来,您的努力最终会浪费掉。简单比复杂更好,除非包装器真的封装了某些东西(即隐藏了底层库),direct比间接更好。

这不是编写意大利面条的许可证 - 您的应用程序仍然需要主要组件的结构和隔离(例如,将UI与您的应用程序提供的实际计算/模拟/文档隔离)。如果你这样做,有一天交换库将是一个可管理的任务,没有任何包装代码。

答案 2 :(得分:2)

你应该在两种情况下包装东西:

  1. 你有理由相信你可能会改变它。我并不是说“好吧,有一天,也许有点儿。”我的意思是你有一些真正的信念,你可能会切换库。或者,如果您需要支持多个库。也许您允许在使用SDL和SFML之间进行选择。或者其他什么。

  2. 您正在提供该功能的抽象。也就是说,你不仅仅是制作一个薄的包装纸;您正在改进该功能。简化界面,添加功能等

答案 3 :(得分:1)

这取决于。

如果您使用的是非常成熟的库,并且可能不会迁移到其他实现,则不需要介体类。例如,你有没有封装stl或boost库?

另一方面,如果您使用的库是新的或者有很多替代品,那么抽象可能会有用。

答案 4 :(得分:1)

是的,你应该编写程序所需的基本功能,然后编写一个包装(冗余......)库来完成你需要的工作。只需在添加新库时,您只需编写一个新的包装器,您只需从程序下面交换底层包装器,而无需关心。如果你把烦恼分开,那么以后更容易添加功能,因为你不必找到你正在使用函数的位置或使用复杂的#ifdef语句来切换库,你只需要使用一些简单的#ifdef来定义像< / p>

#ifdef LIB_A
typedef MyGfxClassA MyGfxClass;
#endif

等...

答案 5 :(得分:1)

如果您想提供更简单的界面(Convention over Configuration),这不是一个坏主意。但是如果你只想提供库中所有实用程序的一对一翻译,那么它就不值得了。