代码之家  ›  专栏  ›  技术社区  ›  flybywire

您是否应该包装您在项目中采用的第三方库?[关闭]

  •  40
  • flybywire  · 技术社区  · 15 年前

    我今天和一个同事讨论过。

    他声称,无论何时使用第三方库,您都应该为它编写一个包装器。因此,您可以在以后随时更改内容,并为您的特定用途提供便利。

    我不同意这个词 总是 ,关于log4j的讨论开始了,我声称log4j已经过了很好的测试和时间验证的API和实现,并且所有可以想到的都可以配置成一个posteriori,并且没有任何东西需要包装。即使您想要包装,也有一些经过验证的包装器,如Commons Logging和Log5J。

    我们在讨论中提到的另一个例子是Hibernate。我声称它有一个非常大的API需要包装。此外,它还有一个分层的API,可以根据需要调整其内部。我的朋友声称他仍然相信应该包装,但由于API的大小,他没有这样做(这个同事比我在当前项目中经验丰富)。

    我声称 this ,在特定情况下应进行包装:

    • 你不确定图书馆如何满足你的需要
    • 您只能使用libary的一小部分(在这种情况下,您只能公开其API的一部分)。
    • 您不确定库的API或实现的质量。

    我还认为,有时您可以包装代码而不是库。例如,将与数据库相关的代码放入DAO层,而不是预先包装所有的休眠。

    好吧,归根结底这不是一个真正的问题,但你的见解、经验和意见是非常感谢的。

    16 回复  |  直到 9 年前
        1
  •  59
  •   Michael Borgwardt    15 年前

    这是一个很好的例子 YAGNI :

    • 这是更多的工作
    • 它夸大了你的计划
    • 它可能会使你的设计复杂化
    • 它没有直接的好处
    • 你为之写作的情景可能永远不会显现出来。
    • 当它完成时,您的包装器很可能需要完全重新编写,因为它与您正在使用的具体库绑定得太紧密,而新的API与您的API根本不匹配。
        2
  •  10
  •   Satanicpuppy    15 年前

    嗯,明显的好处是开关技术。如果有一个库变得不推荐使用,并且希望切换,那么最终可能会重写大量代码以适应更改,而如果对其进行了包装,那么为新的lib编写新的包装器比更改所有代码要容易得多。

    另一方面,这意味着您必须为包含的每个琐碎的库编写一个包装器,这可能是不可接受的开销。

    我的行业都是关于速度的,所以我唯一一次能够证明写一个包装器是正确的,如果它是围绕某个关键库的,那么这个库可能会定期发生显著的变化。或者,更常见的情况是,如果我需要一个新的库并将其转换成旧的代码,这是一个不幸的现实。

    这绝对不是“总是”的情况。这可能是值得的。但是,时间并不总是在那里,而且,最终,如果编写一个包装器需要几个小时,而长期的代码库更改将是很少的,而且是微不足道的……为什么要麻烦呢?

        3
  •  5
  •   bmargulies    15 年前

    这里的问题部分是“包装器”这个词,部分是错误的二分法,部分是JDK和其他所有东西之间的错误区分。

    “包装”一词

    如你所说,把所有的冬眠都包起来是一项完全不切实际的工作。

    另一方面,将休眠依赖项限制到一组已识别、控制的源文件可能是可行的,并获得相同的结果。

    错误的二分法

    错误的二分法是没有认识到第三种选择:标准。如果使用JPA注释,可以将Hibernate替换为其他内容。如果您正在编写一个Web服务并使用JAX-WS注释和JAX-B,那么您可以在JDK、CXF、GlassFish或其他工具之间进行交换。

    错误的区别

    当然,JDK变化缓慢,不太可能死亡。但主要的开源软件包也变化缓慢,不太可能死亡。数不清的成千上万的开发人员和项目使用Hibernate。确实没有更多的休眠消失或者与Java本身发生根本不兼容的API变化的风险。

        4
  •  5
  •   Uri    15 年前

    如果您计划包装的库在同一域中的其他产品的“访问原则、隐喻和习惯用法”中是唯一的,那么您的包装将非常类似于该库,如果有一天切换到其他库,将不会对您有任何好处,因为您需要一个新的包装。

    如果以与其他库类似的方式访问库,并且相同的包装器可以应用于这些库,那么它们可能是基于某些现有标准编写的,并且已经存在一些公共层来访问这两个库。

    只有当我确信必须在生产中支持多个和实质上不同的库时,我才会使用包装器。

        5
  •  5
  •   Finglas    15 年前

    我同意大家所说的一切。

    包装第三方代码的唯一时间是用于单元测试(违反yagni的条)。

    模拟静态等等需要您包装代码,这是为第三方代码编写包装器的一个有效理由。

    在日志代码的情况下,它并不需要。

        6
  •  4
  •   michiel    15 年前

    决定是否包装库的主要因素是库更改对代码的影响。当仅从1个类调用库时,更改库的影响将最小。如果在另一方面,在所有类中都调用了一个库,那么包装器就更有可能被调用。

        7
  •  4
  •   Joel    15 年前
    1. 在项目开始时,应使用原型测试第三方库的可扩展性/适用性/任何其他方面,消除关于选择第三方库的任何不确定性。

    2. 如果您决定继续进行并提供完全的去耦合/抽象支持,那么应该对其进行成本计算,并最终得到项目发起人的批准——最终这是一个商业决策,因为有人必须为其支付费用,并且需要做一些工作(除非它是绝对微不足道的,在这种情况下,API可能是低风险的)。

    3. 一般来说,一个有经验的架构师会选择一种他们可以相当自信的技术,并且他们有经验,并且他们有信心将持续应用程序的生命周期,否则他们将在项目的早期消除决策中的任何风险,从而在大多数情况下消除任何这样做的需要。

        8
  •  4
  •   irreputable    15 年前

    不,Java设计师/想要蜜蜂忙着设计而不是想象的变化。

    有了现代的IDE,当你需要改变的时候,这是小菜一碟。在此之前,保持简单。

        9
  •  3
  •   Shaun    15 年前

    我倾向于同意你的大多数观点。使用绝对命令常常会给你带来麻烦,说你应该“总是”做一些限制你灵活性的事情。我会在你的名单上再加几分。

    当您围绕一个非常常见的API(如Hibernate或log4j)使用包装代码时,您将使新开发人员的出现变得更加困难。新开发人员现在必须学习一个全新的API,如果您没有包装代码,他们马上就会非常熟悉。

    另一方面,您也将开发人员的视图限制在API中。使用API的高级功能需要花费更多的时间,因为您必须确保包装是以能够处理它的方式实现的。

    我看到的许多包装层也非常特定于底层实现。所以,如果您围绕log4j编写一个日志包装器,那么您将用log4j术语进行思考。如果出现了一些新的酷框架,它可能会改变整个范式,因此您的包装代码不会像您想象的那样迁移。

    我肯定不是说包装代码总是不好的,但是正如您所说,有很多因素需要考虑。

        10
  •  3
  •   Bill the Lizard    15 年前

    包装一个经过良好测试和时间验证的第三方库的目的是,将来您可能会决定在某个时间点切换库。包装它可以更容易地进行切换,而无需更改核心应用程序中的任何代码。只需要更改包装器。

    如果你是 绝对肯定 您将永远不会(另一个绝对)在项目中使用不同的日志框架,继续并跳过包装器。即使这样说了,我也可能会拖延写包装纸的时间,直到我 知道 我需要它,就像我第一次需要转换一样。

        11
  •  3
  •   Ken    15 年前

    这是一个有趣的问题。

    我曾在系统中工作过,在我们使用的库中发现了ShowStopper错误,上游不再维护,或者对修复不感兴趣。在像Java这样的语言中,通常无法修复来自包装器的内部错误。(幸运的是,如果它们是开源的,你至少可以自己修复它们。)所以这里没有帮助。

    但是我经常在一种语言中工作,在这种语言中,您可以随时轻松地修改库,而不必看到甚至不需要它们的源代码——例如,我通常向现有类添加新方法。因此,在这种情况下,包装没有意义:只需进行您想要的更改。

    另外,你的同事在所谓的“图书馆”上画了线吗?Java本身呢?他是否包含内置类?他会包装文件系统吗?线程调度程序?内核?(也就是说,用他自己的包装纸——在某种意义上, 一切 是一个围绕CPU的包装器,但听起来他在谈论完全由您控制的源repo中的包装器。)当新版本出现时,我已经有了内置的功能更改或消失。Java是 不受此影响。

    所以这个想法 总是 写一个包装纸就可以下注了。假设他只是在包装第三方图书馆,他似乎在暗中打赌:

    • “第一方”功能(如Java本身、内核等)将永远不会改变。
    • 当“第三方”功能改变时,它总是以一种可以在包装器中修复的方式完成。

    你是真的吗?我不知道。在我做的中大型Java项目中,我很少这样做。我不会花精力去包装所有的第三方库,因为这似乎是一个糟糕的赌注,但你的情况肯定不同于我的。

        12
  •  3
  •   Thorbjørn Ravn Andersen    15 年前

    有一种情况,你有充分的理由可以包装。也就是说,如果你需要测试东西,默认的第三方对象是重的。那么拥有一个接口真的会有很大的不同。

    注意,这不是替换库,而是使它在不太重要的地方可以管理。

        13
  •  2
  •   Bozho    15 年前

    包装整个库是样板文件,在大多数情况下是无效的,也是错误的。这可以用一种非常聪明的方法来完成。我想说,包装一个库主要是在UI组件库的情况下是合适的,同样,您必须添加一些额外的 核心 所有组件都需要您的功能。

    • 如果需要太多的修改和添加,则很可能不是您要查找的库。
    • 如果有适量的添加和修改,那么在这些情况下,设计模式总是很方便的。这个 Decorator pattern (允许动态地将新的/附加的行为添加到现有的对象中),例如,对于大多数情况来说是相当合适的。
    • 如果需要一些重要的更改,并且出现包装对象,则IDE搜索/替换和重构功能提供了一种在所有必需位置更改代码的简单方法。(当然,单元测试在这里很有用;)
        14
  •  1
  •   Ewan Todd    15 年前

    你应该经常、经常、有时、很少或从不这样做。即使是你的同事也不总是这样做,但有指导意义的案例总是而且从来没有。假设它有时是必要的。如果你从来没有包过一个图书馆,最糟糕的后果就是有一天你发现你在这地方使用过的图书馆是必要的。你得花点时间把图书馆包起来,给客户做散弹枪手术。问题是,这种可能性是否会比习惯性地提供很少必要但从未进行过散弹枪手术的包装纸花费更多的时间。

    我的直觉是呼吁雅格尼(你不需要)原则,选择“很少”。

        15
  •  1
  •   TofuBeer    15 年前

    我不会把它包装成一对一的东西,但我会对应用进行分层,以便尽可能多地替换每个部分。这个 ISO OSI 模型适用于所有类型的软件:—)

        16
  •  1
  •   Edward Strange    11 年前

    根据我的经验,如果你充分使用抽象,这个问题就变得相当没有意义了。耦合到库就像耦合到任何其他接口一样。因此,如果需要交换实现,您希望减少意外耦合和重写的必要范围。不要将应用程序逻辑绑定到某个构造上,但不要只是围绕某个东西形成一堆愚蠢的(字面上的)包装器,并期望获得任何好处。

    包装器通常不会获得任何东西,除非它能回答特定的目的(例如,对非多态构造进行多态)。它们经常出现在重构中,但我不建议在它们上面形成体系结构。当然也有一些例外,但是有任何原则。

    这与适配器无关。适配器可以是一个非常重要的组件,当您想要实际更改库的接口时,它的使用可以与项目中的体系结构、代码或域概念保持一致。