公共图书馆是个好主意吗?


16

我一直认为“公共库”是个好主意。我的意思是说,该库包含一些不同应用程序经常需要的通用功能。这样可以减少代码重复/冗余。

我最近读了一篇文章(现在找不到),说这实际上是个坏主意,甚至说这是“反模式”

虽然这种方法有好处。版本控制和变更管理意味着对使用此库的一组应用程序进行回归测试。

对于我的新(Golang)项目,我有些陷入困境。多年来,重复数据删除技术已深深扎根于我,但我觉得这次应该尝试一下。

在撰写本文时,我开始认为这种“通用库”方法是对体系结构进行浏览的结果?也许我的设计需要更多思考?

有兴趣听到想法。


2
我的2美分...如果如果其中一个系统使用它需要对通用api进行更改,则该API或库根本就不是通用库
Raja Anbazhagan

1
我发现代码复制和耦合之间存在根本的权衡。您的问题就是一个很好的例子。该平衡你两个可能会依赖于环境的代码最终会在执行打击之间
乔尔·科内特

据我所知,大多数C开发人员都有一些称为“工具箱”的实用程序。但是,通常不会将其收集到单个包含的库中。更像是“选择”。
Mark Benningfield '18

2
有人打电话给Apache并解决这种疯狂问题。阿帕奇公域
Laiv

Answers:


21

图书馆和再利用绝对是一件好事。它们有一个巨大的缺点,那就是,如果不仔细管理,它们将相当于厨房中装有所有零碎物品的抽屉的功能。

当我负责将整个业务部门有价值的代码的第一个端口(对我来说大部分是新的)移植到64位系统并对我们的构建和打包进行了全面检查时,我看到了这一点*我们倾向于从一堆应用程序中构建出自己的产品,客户会说:“我想要一个执行A,B,D和F以及M的系统和N,您还没有做,将它们全部融合在一起就略有不同。” 所有这些的共同点是一个垃圾抽屉库,该库在过去的几十年中**积累了人们认为应该可重用的所有内容。长话短说,库中的一部分代码不是。我们花费大量宝贵的时间来构建和维护这些依赖项,以便安装公共库,而不是因为我们实际上需要它们。

道理是,图书馆应像对待类一样对待,而不要承担过多的责任。即使您编写的每个程序都使用JSON解析器,也不要将其与线性代数函数放在同一个库中。

将它们保持离散有很多好处,其中最大的好处是,它迫使您的开发人员和包装人员对自己的产品实际需要的内容进行详细的核算,而不是仅包括垃圾箱及其附带的行李。使用内置软件包设置系统时,细粒度的依赖项可确保仅安装必要的部件。即使您忽略存储库并继续编译旧的,笨拙的东西,也不会再有不再使用的东西泄漏到您的发货中。

当然,也有例外,例如将libc很多函数塞入一个库中。在这种情况下,可以推断出这样做的好处,而不是盲目地听从狂热的狂热分子,后者坚持认为除X之外的任何其他方式始终是不好的做法。


*在此过程中,我发现了一个二进制文件,该二进制文件已被传递并且在六年内没有从头开始重新编译。

**数十年的代码没有错。我们有许多关键算法已被充分证明,以至于为了现代性而重写它们都是愚蠢的。


1
我的经验法则是,如果您打算将库命名为“ common”之类的东西,那肯定会遇到麻烦。
Karl Bielefeldt

9

令人尴尬的是,在几十年前的团队环境中,我引入了一个名为“ common”的通用库。我当时还不太了解那种松散协调的团队环境在短短几个月内会发生什么变化。

当我介绍它时,我想我已经说清楚了,也记录了它是出于我们都同意的事情,我们发现它每天都有用,它打算成为一个极简主义的库,并且该库除了标准库,以便在新项目中尽可能容易地进行部署。当时我的想法是,它只是对标准库的少量扩展,对于我们在特定领域每天发现的有用的东西。

它开始得很好。我们从common/math*日常使用的例程数学库()开始,因为我们从事的是计算机图形学,而计算机图形学经常是线性代数的繁重工作。因为我们经常与C语言代码interoping,我们商定了一些有用的实用功能,如find_index它不同于std::find在C ++中,它将返回序列中找到的元素的索引,而不是迭代器,该迭代器模仿了我们的C函数的工作方式(这种事情)-有点折衷,但极简且广泛使用,足以使每个人都熟悉和实用,并且即时熟悉是我认为要尝试做出“常见”或“标准”事物的一个非常重要的标准,因为如果它确实是“常见”,则由于它的广泛性,它应该具有那种熟悉的质量采用和日常使用。

但是随着时间的流逝,人们开始添加自己个人使用的东西时,图书馆的设计意图突然消失了,他们只是认为自己可能对其他人有用,却找不到其他人使用它。后来有人开始为通用GL相关例程添加依赖OpenGL的函数。进一步,我们采用了Qt,人们开始添加依赖于Qt的代码,因此,公共库已经依赖于两个外部库。有时,有人添加了依赖于我们特定于应用程序的着色器库的通用着色器例程,到那时,如果不引入Qt,OGL和我们特定于应用程序的着色器库并编写代码,您甚至无法将其部署到新项目中项目的重要构建脚本。因此变成了折衷的,相互依存的混乱。

但是我还发现,通过辩论应该进入和不应该进入该库的内容,如果您没有设定严格的规则来确定“共同”是什么,那么被认为是“共同”的内容很容易变成一个非常主观的想法每个人每天都会发现有用的东西。对标准的松动并迅速从每个人的东西降解觉得有用的每天的东西在一个单一的开发者发现有用可能有被别人有益的可能性,而在这一点上库退化为一个折衷的混乱真快。

但是,此外,当您达到这一点时,一些开发人员可以出于不喜欢编程语言的简单原因开始添加内容。他们可能不喜欢for循环或函数调用的语法,这时库开始充斥着与语言的基本语法不符的内容,替换了几行简单的代码,这些代码实际上并没有将任何逻辑都复制到一个简短的外来代码行中,只有引入此类速记的开发人员才熟悉。然后,这样的开发人员可能会开始向使用此类速记实现的通用库添加更多功能,在这一点上,公共库的重要部分与这些奇特的速记方式交织在一起,对于介绍它的开发人员而言,它们看起来似乎很漂亮和直观,但它们却丑陋而陌生,其他人很难理解。在这一点上,我认为您知道,做出真正“普通”的东西的任何希望都将丢失,因为“普通”和“陌生”是相反的想法。

因此,至少在一个松散协调的团队环境中,那里有各种各样的蠕虫罐,其库的野心与“常用的东西”一样广泛和笼统。尽管根本的问题可能是首先是松散的协调,但至少有多个旨在实现更单一目的的库(例如旨在提供数学例程的库,而没有别的什么),就其功能而言,降级的幅度可能不会很大设计纯度和依赖性作为“公共”库。因此,回想一下,我认为最好是在库中使用更清晰的设计意图。多年来,我还发现目的狭窄和适用范围狭窄是根本不同的想法。

同样,我也承认至少有点不切实际,对美学的关注可能有点过分,但是我倾向于以图书馆最薄弱的环节来判断我对图书馆质量(甚至是“美”)的看法的方式更多它的最强功效,就像您向我展示世界上最美味的食物,但是在同一盘子上放一些腐烂的东西,闻起来真的很难闻,我倾向于拒绝整个盘子。而且,如果您在这方面像我一样,并且做一些可以引起各种添加的事情,称为“常见”,那么您可能会发现自己看上去像板块,侧面有些腐烂。因此,同样地,我认为以某种方式对图书馆进行组织,命名和记录是一件好事。随着时间的推移邀请越来越多的人加入。这甚至可以应用于您的个人创作,因为我当然在这里和那里创造了一些烂东西,如果不加到最大的盘子里,它的“污点”要少得多。如果仅凭纯粹的优点,即开始耦合所有内容变得不那么方便,将事物分离到很小的,非常单一的库中也倾向于更好地解耦代码。

多年来,重复数据删除技术已深深扎根于我,但我觉得这次应该尝试一下。

在您的情况下,我可能建议您着手简化重复数据删除技术。我并不是说要复制并粘贴经过大量测试的,容易出错的代码的大片段,或类似的东西,也不是复制大量可能在将来需要进行更改的非平凡代码。

但是,尤其是如果您有创建“通用”库的心态,我认为您希望创建一个广泛应用,高度可重用的库,也许理想情况下,您发现今天的库与现在十年后的库一样有用,那么有时您甚至可能需要重复一些操作来达到这种难以捉摸的质量。因为复制实际上可能充当一种去耦机制。就像您要将视频播放器与MP3播放器分开一样,那么您至少必须复制一些东西,例如电池和硬盘驱动器。他们无法共享这些东西,否则它们将不可分割地耦合在一起,无法彼此独立使用,届时人们可能不再对设备感兴趣,如果他们只想播放MP3。但是,在将这两个设备分开后的一段时间,您可能会发现MP3播放器可以受益于与视频播放器不同的电池设计或更小的硬盘驱动器,这时您不再需要复制任何东西。最初以复制开始,以允许该相互依赖的设备拆分为两个独立的独立设备,后来可能会产生完全不再冗余的设计和实现。

值得从使用库的角度考虑事物。您是否真的想使用一个最小化代码重复的库?您可能不会这样做,因为这样做自然会依赖于其他库。那些其他库可能会依赖其他库来避免重复其代码,依此类推,直到您可能需要导入/链接50个不同的库以仅获得一些基本功能(如加载和播放音频文件),这变得非常笨拙。同时,如果这样的音频库故意选择在这里和那里复制某些内容以实现其独立性,那么它在新项目中的使用将变得非常容易,并且有可能不需要更新频率就几乎可以了,因为它会由于需要更改其依赖的外部库,因此可能需要更改,而这些库可能正在试图实现比音频库所需的更通用的目的。

因此,有时值得故意选择重复一点(有意识地,从不懒惰-实际上是出于勤奋),以使库解耦并使其独立,因为通过这种独立性,它实现了更广泛的实际适用性和甚至稳定(不再有传入耦合)。如果您想设计最可重复使用的库,它将使您从一个项目持续到下一个项目并持续多年,那么除了将其范围缩小到最小之外,我实际上建议考虑在此处进行一些重复。并且自然地编写单元测试,并确保它确实经过了全面的测试,并且可以可靠地执行。这仅适用于您真正想花一些时间来概括到远远超出单个项目的范围的库。


3

您可以考虑将三类函数放入库中:

  1. 值得大家重复使用的东西。
  2. 仅值得您的组织重复使用的东西。
  3. 东西不值得重复使用。

第一类是应该存在标准库的东西,但由于某种原因,没人绕开它(或者有人吗?您是否进行了彻底搜索?)。在这种情况下,请考虑使您的库开源。分享您的工作不仅对他人有帮助,而且对您也有帮助,因为您将收到其他用户的错误报告和补丁。如果您怀疑有人会为您的库做出贡献,那么您可能正在处理的功能实际上是类别2或类别3。

第二类是您一遍又一遍需要的东西,但是世界上没有其他人需要它。例如,执行模糊的网络协议以与内部开发的后端系统进行通信。在这种情况下,将这些内容放入内部库中以提高新应用程序的开发速度可能是有意义的。只要确保它不会受到特征爬移的太大影响,并开始包含实际上属于类别1或3的内容即可。此外,Blrfl关于模块化的建议非常好:不要创建一个整体式Conor Corp.库。为多个单独的功能创建多个单独的库。

类别3是一种功能,要么实现起来很琐碎,以至于不值得将其移至库中,要么无法确定在其他应用程序中是否会再次以这种形式再次使用它。此功能应保留为其开发的应用程序的一部分。如有疑问,它可能属于此类。


1

几乎所有语言都有一个通用/标准库,因此被广泛认为是一个好主意。通常,将第三方库用于各种任务而不是重新发明轮子也是一个好主意,尽管显然应该在每种情况下评估成本/收益和库的质量。

然后是单个开发人员或机构在不相关的项目中使用的“通用实用程序”库。这是怎样的库的可以被认为是一个反模式。在我所看到的情况下,这些库只是以非标准且记录不良的方式从标准库或更知名的第三方库复制功能。


these libraries just replicate functionality from standard libraries这并不是一件坏事,在javascript中,您添加了已经实现现有功能的库,以增加对旧js引擎的支持,还为旧的sdk等添加了android支持库
。– svarog

@svarog:您是否在考虑“ polyfills”,它模仿了本机不支持的旧引擎的新标准中的功能?我看不出有任何理由自己编写这些文件,因为有众所周知的开源库可用于这些目的。
JacquesB

0

跨团队共享的大多数库引起的问题多于解决的问题。“通往地狱的道路是有良好意图的。”

满足以下大多数条件的库是例外:

  • 有可靠的长期维护资金
  • 拥有专门的支持团队/社区
  • 有一个错误跟踪器
  • 有广泛的测试范围
  • 有一个明确的目的
  • 除了标准库或准标准库之外,它们自身没有依赖项(在构建和运行时)
  • 明确区分公共API和内部
  • 有一个沟通渠道和过程供所有/许多用户就新功能和版本达成协议

在典型的(非初创)公司中,团队之间共享的库几乎没有上述条件。这是因为大多数公司团队都获得报酬来交付产品,而不是图书馆。有些公司确实拥有成功的共享策略,例如Google的monorepo,但这需要在构建和测试基础结构方面进行大量投资。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.