依赖包装了还是提示不存在

包裹您的依赖关系如何使您免于将来的痛苦 (How Wrapping Your Dependencies Will Save You From Future Suffering)

A few months back, I was talking with one of the other developers on our team and somehow we got onto the topic of what I thought my biggest learning in the past couple years was at our company. I’d been working as a software developer for the past 6 years and at Resource for just over 2 years.

几个月前,我正在与团队中的其他开发人员进行交流,不知何故,我们进入了一个主题,我认为过去几年中我最大的学习是在我们公司。 在过去的6年中,我一直从事软件开发工作,而在Resource的工作中,我已经工作了2年多。

“Wrap your dependencies,” I told my coworker with the sort of gaunt stare you expect to see from a grizzled war veteran.

“包裹你的依赖物,”我告诉我的同事,你期望从灰暗的退伍军人身上看到那种的凝视。

Despite the fact that I’ve been writing software for over half a decade, I didn’t learn this lesson until more recently. Prior to working at Resource, I worked at a company doing full stack work with Vue.js on the frontend and a Django backend. During my time there, I primarily dealt with dependencies from PyPI and occasionally dealt with the npm ecosystem for adding a component library or a small utility library. This all changed once I started working at Resource. When I initially joined, the code base was written using MeteorJS. Shortly after I began, we migrated to a React/Node stack. It didn’t take long for me to realize that a lot of what I’d been told by friends and colleagues about the quality of npm packages was true: a lot of packages are just shit.

尽管我从事软件开发已经超过五年了,但直到最近我才学到这一课。 在Resource工作之前,我曾在一家公司从事前端和Django后端Vue.js的全栈工作。 在此期间,我主要处理来自PyPI的依赖关系,偶尔处理npm生态系统以添加组件库或小型实用程序库。 当我开始在Resource工作时,这一切都改变了。 当我最初加入时,代码库是使用MeteorJS编写的。 在我开始之后不久,我们迁移到了React / Node堆栈。 不久之后,我意识到朋友和同事对npm软件包质量的许多要求是真实的:很多软件包只是狗屎。

Historically I’ve tended to be the sort of developer who has leaned into using existing packages when possible. I have an aversion to reinventing wheels and abhor NIH syndrome. Yet this attitude definitely caused me some pain once I was working in the npm ecosystem day in and day out.

从历史上看,我一直是倾向于尽可能使用现有软件包的开发人员。 我对重塑车轮和可恶的NIH综合征不满意。 但是,一旦我日复一日地在npm生态系统中工作,这种态度肯定使我感到有些痛苦。

After spending far more time coping with this pain than I’m proud to admit, we began to adopt a strategy that would save us a lot of heartache: we decided to start wrapping the third party dependencies in our code.

在花了更多的时间来应对这种痛苦之后,我们开始采取可以节省很多心痛的策略:我们决定开始将第三方依赖项包装到我们的代码中。

Note: I use the word wrapper in this post to describe a group of design patterns that involve adding some abstraction between your code and a third party dependency. People sometimes use words like “adapter” or “facade” to indicate wrapping code with a certain intent. Does this code need to adapt its arguments to interact with some other code, or am I creating a facade in front of this other module to simplify its interface? When I say “wrapper” for the rest of this article, I use it as an all encompassing term which includes these patterns and doesn’t worry too much about intent.

注意:我在这篇文章中使用包装器一词来描述一组设计模式,这些设计模式涉及在代码和第三方依赖项之间添加一些抽象。 人们有时会使用诸如“ adapter”或“ facade”之类的字眼来表示包装代码。 该代码是否需要调整其参数以与其他代码交互,还是我要在其他模块之前创建外观以简化其接口? 当我在本文的其余部分说“包装”时,我将其用作包含这些模式的全面术语,而不必太担心意图。

设置 (The Setup)

The first time you’ll regret not wrapping your dependencies is probably gonna play out like the following. One day, you need some common functionality that you suspect exists in a third party library. You dutifully search npm until you find a library, such as request, which provides a simple API to make HTTP requests. You add it to your project and don’t really think about it.

第一次您会后悔没有包装依赖项可能会像下面这样玩。 有一天,您需要怀疑第三方库中存在的一些常用功能。 您会认真搜索npm,直到找到一个库(例如request) ,该库提供了用于发出HTTP请求的简单API。 您将其添加到您的项目中,而不必真正考虑它。

Months or maybe years later, something unthinkable happens; request has become deprecated, with no plans to add any new features and no guarantees that bugfixes or security patches will be merged.

几个月或几年后,发生了一些不可思议的事情; 该请求已被弃用,没有计划添加任何新功能,也没有保证将合并错误修正或安全补丁。

You after reading your library is being deprecated
您在看完图书馆后就不赞成使用

Uh-oh. That’s not good. Guess you better find a replacement for it. You select what seems to be a popular HTTP library, cross-fetch.

哦哦 这不好。 猜猜您最好找到它的替代品。 您选择似乎流行的HTTP库cross-fetch

Unfortunately, the API for request and cross-fetch is fairly different. Request has convenience methods for each HTTP method, whereas cross-fetch expects a url and a configuration object with a method key. Request takes a json parameter, whereas cross-fetch expects you to call .json() on the response. And so the list goes on.

不幸的是,用于请求交叉获取的API完全不同。 对于每个HTTP方法, 请求都有方便的方法,而交叉提取需要一个url和一个带有method键的配置对象。 请求采用json参数,而交叉提取则希望您在响应中调用.json() 。 因此,清单继续。

If you have good tooling and aren’t doing anything too crazy with your build system, this might not be too difficult of a change. You might start by doing some form of a crude find and replace, and after that start manually inspecting all the places where you previously used request. In a large codebase, this can be a nightmare and can be quite risky.

如果您拥有良好的工具,并且没有对构建系统做任何疯狂的事情,那么进行更改可能不会太困难。 您可能会先进行某种形式的粗略查找和替换,然后开始手动检查先前使用request的所有位置。 在大型代码库中,这可能是一场噩梦,而且风险很大。

This is one of the best reasons to wrap your dependencies: it makes replacing a dependency a much easier task.

这是包装依赖项的最佳理由之一:它使替换依赖项变得容易得多。

两次进口的故事 (A Tale of Two Imports)

Now imagine that when you had first decided you needed an HTTP library, you had instead wrapped the request library with a module of its own. You can envision it looks something like the following.

现在想象一下,当您最初决定需要一个HTTP库时,而是使用一个自己的模块包装了请求库。 您可以设想它看起来像以下内容。

At first, this looks like a fairly useless module. Functionally, it does the same thing, but now instead of writing an import like import request from 'request' all over our codebase, you instead write import myRequest from 'myrequest.js'.

最初,这看起来像是一个毫无用处的模块。 从功能上讲,它执行相同的操作,但是现在,您无需在整个代码库中编写像import request from 'request'一样的import request from 'request' ,而是import myRequest from 'myrequest.js'编写import myRequest from 'myrequest.js'

Eventually, when that day arrives where you need to replace request with cross-fetch, you now only need to make changes in this one file. You just need to adapt the arguments that the request API takes and transform them into the shape that cross-fetch expects. The following gist shows how this might look.

最终,当那天到达需要用交叉获取替换请求的地方您现在只需要在一个文件中进行更改。 您只需要调整请求 API接受的参数,并将其转换为交叉获取所需的形状即可。 以下要点显示了外观。

It’s definitely a bit painful to write that code. But it’s a lot easier than trying to find all the places in the codebase where you’re making HTTP requests and updating them to conform to the API of cross-fetch. It’s also a lot safer.

编写该代码肯定有点痛苦。 但这比尝试在代码库中查找发出HTTP请求并更新它们以符合cross-fetch API的地方容易得多。 这也更安全。

Not only are you now able to replace this dependency when you have to, such as for security reasons, but it also gives you the flexibility to replace it for other reasons. Perhaps you discover that one of these code paths is really critical to your product and is too slow. You go check npm and discover there’s a fetching library that performs better which can be swapped in.

现在,不仅可以出于安全原因而在必要时替换此依赖关系,而且还可以灵活地出于其他原因替换它。 也许您发现这些代码路径之一对您的产品确实至关重要,而且速度太慢。 您去检查npm并发现有一个性能更好的可取库。

Or maybe you run into a bug while the maintainer of the library is on vacation. Ideally, you’d fix the bug in the source of the library itself and submit a pull request to the project. But if you need the fix immediately, you can easily patch it at the source inside of your wrapper.

或者,也许在库的维护人员在休假时遇到错误。 理想情况下,您将修复库本身的源中的错误,然后向项目提交请求请求。 但是,如果您立即需要修复程序,则可以在包装器内部的源代码中轻松对其进行修补。

However, being able to easily replace a third party dependency isn’t the only benefit using a wrapper confers.

但是,能够轻松替换第三方依赖项并不是使用包装器的唯一好处。

当好的测试变坏时 (When Good Tests Go Bad)

In the scenario without the wrapper, you probably noticed something else while replacing all the call sites to now use the cross-fetch API. You made your changes and then saw a bunch of tests break. This is because a bunch of tests were mocking the request dependency, but are now trying to use cross-fetch. Now you’re stuck updating a bunch of test code, even though you haven’t changed the actual business logic that makes HTTP requests.

在没有包装的情况下,当替换所有调用站点以现在使用交叉获取 API时,您可能会注意到其他情况。 您进行了更改,然后看到大量测试中断。 这是因为大量测试都在嘲笑请求依赖关系,但现在正尝试使用cross-fetch 。 现在,即使您没有更改发出HTTP请求的实际业务逻辑,也要更新一堆测试代码。

There’s an adage often stated when it comes to testing: Don’t mock what you don’t own. The idea is that if you don’t maintain the source code to something, you should not be mocking it.

在测试时经常有句格言:不要嘲笑自己不拥有的东西。 这样的想法是,如果您不维护某些源代码,则不应嘲笑它。

By mocking the request library in tests, this adage was broken, and you’re paying the price. Now you must go and update all the calls to mock request and instead mock cross-fetch. Ugh.

通过在测试中模拟请求库,此谚语被打破了,您要付出代价。 现在,您必须去更新所有对模拟请求的调用,而不是模拟cross-fetch 。 啊。

In the scenario with the wrapper, you can avoid this pain by simply mocking the wrapper. Instead of application code like the following:

在使用包装器的情况下,您可以通过简单地模拟包装器来避免这种痛苦。 代替如下的应用程序代码:

You would just reference the wrapper in your application code:

您只需在应用程序代码中引用包装器即可:

In the tests, you would now mock out the wrapper with a statement like jest.mock('./wrappers/request.js'). If you went with this approach, when swapping the request dependency for cross-fetch you wouldn’t break any tests.

在测试中,您现在将使用诸如jest.mock('./wrappers/request.js')类的语句来模拟包装jest.mock('./wrappers/request.js') 。 如果采用这种方法,则在将请求依赖项交换为交叉获取时,您不会破坏任何测试。

This should raise some eyebrows though… If you’re mocking the wrapper, won’t the tests fail to detect if the wrapper incorrectly adapted the request API to the cross-fetch API?

但是,这应该引起一些注意...如果要嘲笑包装器,测试是否不会检测到包装器是否错误地将请求 API适应了交叉获取 API?

Yep.

是的

This is why you MUST write integration tests for the wrapper. Ideally, you want to write tests for the wrapper that don’t mock anything and verify that swapping one third party implementation for another doesn’t break anything. In some cases this is more feasible than others. When writing an HTTP client wrapper, as in this example, instead of mocking the third party dependencies, you can use something like pollyjs to mock everything at the HTTP layer.

这就是为什么您必须为包装编写集成测试的原因。 理想情况下,您想为包装程序编写不模拟任何内容的测试,并验证将一个第三方实现换成另一个不会破坏任何东西。 在某些情况下,这比其他情况更可行。 如本例所示,在编写HTTP客户端包装时,您可以使用pollyjs之类的东西来模拟HTTP层中的所有内容,而不必模拟第三方的依赖关系。

There’s also another, more insidious, scenario that you can run into when mocking third party dependencies. Imagine a scenario where there wasn’t a wrapper, you had mocked out request, and instead of trying to replace request with cross-fetch you were just looking to upgrade request from version 2.81.0 to version 2.82.0.

在嘲笑第三方依赖项时,还会遇到另一个更隐蔽的场景。 试想一下,那里是不是包装的场景中,你已经嘲笑了请求 ,而不是试图取代请求 跨接你只是想升级从版本请求 2.81.0到版本2.82.0

You bump the library, run the tests, and everything passes. Great! You merge the code and push to production only to watch all hell break loose.

您打开库,运行测试,一切都通过了。 大! 您合并代码并推入生产阶段只是为了观看所有地狱的事情。

That’s because 2.82.0 introduced a breaking change, but since you were mocking the request library and never executed any of it’s code during the tests, they pass with flying colors.

这是因为2.82.0引入了重大更改,但是由于您在嘲笑请求库,并且在测试过程中从未执行过任何代码,因此它们以飞色传递。

Again, this is a scenario that could be avoided by wrapping the dependency and writing integration tests for the wrapper. In that case, the tests that mocked the wrapper would happily pass, and the integration test would fail hard due to the breaking change.

同样,可以通过包装依赖关系并为包装器编写集成测试来避免这种情况。 在那种情况下,嘲笑包装程序的测试将很高兴通过,并且由于突破性更改,集成测试将很难失败。

为了形成更完善的API (In Order to Form a More Perfect API)

One of the harder to quantify benefits of using a wrapper is that you can more accurately design an API based on your application’s actual needs. Although third party dependencies save time on implementation, they aren’t a substitute for design. In many cases, a third party dependency has discovered its API through lots of battle tested use. But often you’ll find that how your application specifically needs to use it is a little different than how the library has been designed.

量化使用包装器的好处之一是,您可以根据应用程序的实际需求更准确地设计API。 尽管第三方依赖项可以节省实施时间,但它们并不能替代设计。 在许多情况下,第三方依赖项已经通过大量的经过战斗测试的使用发现了其API。 但是通常您会发现应用程序特别需要使用它的方式与库的设计方式有些不同。

Let’s go back to looking at request. For this example, we’ll be looking at how you would set up a scraping application that needs to specify some different sets of proxy configuration.

让我们回到请求 。 对于此示例,我们将研究如何设置需要指定一些不同的代理配置集的抓取应用程序。

Let’s assume that you have a number of different scraping “profiles” that you wish to use. Each of these profiles will configure a couple things, such as a proxy server to use, some specific HTTP headers, and some specific authentication credentials. For example, you’ll have a US-EAST profile that requires one specific Authorization header for that proxy, and a US-WEST profile that requires a different Authorization header.

假设您要使用许多不同的抓取“配置文件”。 这些配置文件中的每一个都将配置一些内容,例如要使用的代理服务器,某些特定的HTTP标头和某些特定的身份验证凭据。 例如,您将拥有一个US-EAST配置文件,该配置文件要求该代理具有一个特定的Authorization标头,以及一个US-WEST配置文件,其需要一个不同的Authorization标头。

Requiring the calling code to specify profile specific Authorization headers everywhere you want to make an HTTP request is not ideal. Instead, you can just define each of these profiles with a name and its configuration inside the wrapper file. You’ll then modify the wrapper API to accept a profile parameter. This way, each of the calling sites doesn’t need to worry about the nitty-gritty details of HTTP headers but can instead just specify a specific profile to use.

在每个要发​​出HTTP请求的地方都要求调用代码指定配置文件特定的Authorization标头并不理想。 相反,您只需在包装文件中使用名称及其配置定义每个配置文件。 然后,您将修改包装器API以接受profile参数。 这样,每个调用站点都不必担心HTTP标头的细节,而只需指定要使用的特定配置文件即可。

The result is an API that better suits the use case which means that testing will be easier, and making modifications to calling code should be less coupled. Better yet, there are more clearly defined boundaries for the system such that any implementation concerns about how profiles are handled don’t leak into tests.

结果是一个更适合用例的API,这意味着测试将更容易,并且对调用代码的修改应减少耦合。 更好的是,为系统定义了更清晰的边界,因此任何有关配置文件处理方式的实现问题都不会泄漏到测试中。

只有西斯工程师才能做到绝对 (Only Sith Engineers Deal in Absolutes)

You’re either with wrappers or you’re their enemy
你要么带着包装纸,要么是他们的敌人

Of course, like any choice in software design, there’s always trade-offs to using wrappers. For each case, you’ll want to ask yourself a few questions to determine if building a wrapper for a dependency is really worth its drawbacks.

当然,就像软件设计中的任何选择一样,在使用包装器时总是要权衡取舍。 对于每种情况,您都需要问自己几个问题,以确定为依赖项构建包装器是否真的值得其缺点。

At a minimum, adding a wrapper always adds extra work. There’s extra indirection, and now a developer can’t just go read the docs for the dependency to understand how it works. They might need to look at the source for the wrapper to really understand what contract the wrapper API provides. Good tooling can help you out a lot here. Using a fully featured IDE will provide things like tooltips with parameters and docstrings.

至少,添加包装器总是会增加额外的工作。 还有额外的间接方式,现在开发人员不能只阅读文档中的依赖项以了解其工作原理。 他们可能需要查看包装器的来源,才能真正了解包装器API提供的合同。 好的工具可以在这里为您提供很多帮助。 使用功能齐全的IDE将为工具提示等提供参数和文档字符串。

Adding a wrapper usually requires adding more documentation as well. Since you’re not dealing with the third party package directly, looking at those docs may be misleading, depending on what has been implemented in your wrapper. With modern tooling, I don’t tend to think this is that big of a deal. Using a static analysis tool like Typescript or Flow will go a long way towards telling you if you try to do anything too stupid.

添加包装程序通常还需要添加更多文档。 由于您不是直接与第三方软件包打交道,因此根据包装程序中实现的内容,查看这些文档可能会产生误导。 使用现代工具,我不倾向于认为这有什么大不了的。 使用诸如Typescript或Flow之类的静态分析工具将对告诉您是否尝试做任何愚蠢的事情大有帮助。

As I mentioned earlier, you’ll always want to make sure you test this wrapper, which is another burden added to your codebase.

如前所述,您将始终要确保测试此包装器,这是添加到代码库中的另一个负担。

So when is this burden not worth it? I typically ask myself a few questions.

那么什么时候不值得负担呢? 我通常会问自己几个问题。

How close is this code to my core business logic? Is the code that uses this dependency or wrapper likely to change, or are my needs likely to change?

该代码与我的核心业务逻辑有多近? 使用此依赖项或包装的代码是否可能会更改,或者我的需求是否可能会更改?

The less I understand the API contract I need, the less likely I am to commit to a wrapper right away. Otherwise, it can be a lot of wasted work that will just be thrown out when I discover that I actually need something totally different.

我对所需的API合同了解得越少,就越不可能立即提交给包装器。 否则,当我发现我实际上需要完全不同的东西时,可能会浪费很多工作。

Is this a “commodity” library? Are there other libraries that exist which provide a similar service at the same level of abstraction?

这是“商品”图书馆吗? 是否存在其他提供相同抽象级别相似服务的库?

Generally, the more of a “commodity” a library is, the more likely I am to wrap it. If a lot of libraries work at the same level of abstraction, there’s a good chance its roughly correct, and wrapping will allow me to replace or extend the abstraction to suit my application.

通常,图书馆的“商品”越多,我包装它的可能性就越大。 如果许多库在相同的抽象级别上工作,则很有可能大致正确,而包装将使我可以替换或扩展抽象以适合我的应用程序。

Is this an implementation detail? Is this just data manipulation calls to a functional library like Lodash?

这是实现细节吗? 这仅仅是对Lodash之类的功能库的数据操作调用吗?

Probably not worth wrapping.

可能不值得包装。

Is this just too coupled/difficult/infeasible to wrap?

这是否太耦合/困难/难以解决?

For something like an ORM, I would probably end up effectively implementing an in-house ORM just by trying to wrap an existing one. In practice, codebases rarely decide to actually swap out one ORM in favor of another, so little benefit is gained.

对于诸如ORM之类的东西,我可能仅通过尝试包装一个现有的ORM就可以有效地实现内部ORM。 在实践中,代码库很少决定将一个ORM换成另一个,因此收效甚微。

Is this an API client library, like Github’s octokit?

是像Github的 octokit 一样的API客户端库 吗?

Choosing to write a wrapper here depends on a few things. Using the example of octokit, if you plan on supporting multiple git hosting providers, you’ll almost certainly be best served by writing an abstraction that sits above the specific library which could handle multiple providers. But if you’re just using a library to fetch data without a better abstraction, it might be overkill to wrap it. However, wrapping it will still prevent you from accidentally mocking it and dealing with false positive tests.

选择在此处编写包装程序取决于几件事。 以octokit为例,如果您计划支持多个git托管提供程序,则可以肯定地通过编写位于可以处理多个提供程序的特定库之上的抽象来最好地为您服务。 但是,如果您只是在使用库来获取数据而没有更好的抽象,则将其包装起来可能会过大。 但是,包装它仍然可以防止您意外地嘲笑它并处理假阳性测试。

Some examples that come to mind which are good candidates for wrapping are: logging, HTTP requests, ui elements/components, generic string utils, or transactional email sending.

想到的一些适合打包的示例包括:日志记录,HTTP请求,ui元素/组件,通用字符串utils或事务性电子邮件发送。

You might have noticed that I suggest wrapping “commodity” libraries, like making HTTP calls, and I also suggest designing more application specific APIs as a benefit of wrapping. How can we wrap something that should be a fairly generic operation and yet also make its API more specific to our application’s needs? Just add another layer of wrappers! Sort of. If you find yourself with a wrapper API that isn’t quite at the right abstraction, feel free to move the wrapper down. For example, if your application had an HTTP wrapper that always added a specific header, and then you discovered that in one place, you need to make HTTP calls without that header, it’s perfectly okay to factor out a lower level wrapper from your initial wrapper which can be reused in places that don’t need the extra header. The important part is to make sure that you’re still only importing a third party dependency in one place.

您可能已经注意到,我建议包装“商品”库,例如进行HTTP调用,并且我还建议设计更多应用程序特定的API,这是包装的好处。 我们如何包装应该是相当通用的操作,同时又使其API更符合我们应用程序需求的东西? 只需添加另一层包装器即可! 有点。 如果发现包装器API的抽象性不正确,请随意将包装器下移。 例如,如果您的应用程序具有始终添加特定标头的HTTP包装器,然后您发现某个地方需要在没有该标头的情况下进行HTTP调用,那么从初始包装器中剔除较低级别的包装器是完全可以的可以在不需要额外标题的地方重复使用。 重要的部分是确保您仍然只在一个位置导入第三方依赖项。

“实施细节” (“Implementation Details”)

In our case, we’ve found that the best way to implement this strategy is to take advantage of yarn workspaces and build internal wrapper packages that provide clear boundaries. This makes them easy to package and distribute between multiple projects that we maintain.

在我们的案例中,我们发现实施此策略的最佳方法是利用纱线工作区并构建内部包装程序以提供清晰的边界。 这使它们易于打包并在我们维护的多个项目之间分发。

If you’re not familiar, yarn workspaces provide a way to easily manage multiple packages within the same repository. Even if you have no intention of publishing these packages to a package registry, enforcing some isolation via workspaces can help ensure there isn’t too much coupling between discrete components.

如果您不熟悉,纱线工作区提供了一种轻松管理同一存储库中多个卷装的方法。 即使您无意将这些软件包发布到软件包注册表中,通过工作空间强制进行一些隔离也可以帮助确保离散组件之间没有太多的耦合。

To start using an HTTP wrapper, all we had to do was to add a new package to our monorepo: @resource/http . Now, we only import @resource/http wherever we want to make HTTP calls. If we ever find ourselves again needing to swap implementations for a different HTTP library, rather than having to update call sites everywhere, we only have to update @resource/http to coerce our wrapper API to the newly swapped implementation. This also means we can write tests mocking out @resource/http, and we don’t have to be afraid that the API signature will change on us, since we control it.

要开始使用HTTP包装器,我们要做的就是将一个新包添加到我们的monorepo中: @resource/http 。 现在,我们仅在要进行HTTP调用的位置导入@resource/http 。 如果我们再次发现需要交换其他HTTP库的实现,而不必在各处更新调用站点,则只需更新@resource/http即可将包装器API强制为新交换的实现。 这也意味着我们可以编写模拟@resource/http测试,并且我们不必担心API签名会发生变化,因为我们可以控制它。

The important thing here is that we have the freedom and control to patch, fix, replace, and otherwise alter how we make HTTP calls without having to modify all the call sites in our codebase.

这里重要的是,我们可以自由和控制地修补,修复,替换以及以其他方式更改我们进行HTTP调用的方式,而不必修改代码库中的所有调用站点。

这是一个包装(每) (And That’s a Wrap(per))

Although you shouldn’t always wrap your dependencies, you should use your judgement to determine if it’s the right move while writing code. When determining if I should wrap something, I always ask myself:

尽管您不应该总是包装依赖项,但是您应该根据自己的判断来确定编写代码时是否正确。 在确定是否应该包裹东西时,我总是问自己:

  • Is this a “commodity” library?这是“商品”图书馆吗?
  • Do I have a good understanding for what abstraction I need in my application?我对我的应用程序需要什么抽象有很好的了解?
  • Am I sure this dependency isn’t too coupled or too much of an implementation detail?我确定这种依赖性不是太耦合或实现细节太多吗?

If I can confidently answer yes to those three questions, it’s a good indicator that the extra work of a wrapper will be more than worth it.

如果我可以肯定地回答这三个问题,则可以很好地表明包装程序的额外工作将是值得的。

翻译自: https://levelup.gitconnected.com/why-you-should-often-wrap-your-dependencies-5fced2999616

依赖包装了还是提示不存在


http://www.taodudu.cc/news/show-3465388.html

相关文章:

  • 不会吧不会吧?真的有人认为程序员很轻松么!如何对抗编码焦虑?
  • 解救
  • 压缩包加密后门_什么是加密后门?
  • 英语演讲 | 清华大学留学生伊瓦娜2020年毕业演讲:我从未见过任何一个国家像中国这样
  • CoPuppy多重元素构建“DEFI+NFT”元宇宙
  • 2万字高频MySQL面试题总结(含答案),金九银十成为offer收割机!【建议收藏】
  • 女博士的生活是怎么样的?
  • 引领汽车潮改新风向,看“菱大师”柳州炫技
  • 高级测试工程师的简历长啥样?
  • python实现冒险者游戏(文字版,无界面)
  • Stable Diffusion背后公司再融1亿美金,网友:资本的盛宴,艺术家却分不到一杯羹?...
  • 基于python的风险管理方式属于_张家港高校邦_Python科学计算_网课答案
  • 《波斯战火》第一个世界帝国及其西征
  • 昆明皮肤病专科医院
  • 我的世界java什么村民卖地图_教程/村民交易大厅
  • 南瑞服务器装系统认不到硬盘,南瑞集控系统介绍.
  • 说说事业单位教师入编体检详细流程怎样预防体检不合格
  • python类中包含一个特殊的变量、它可以访问类的成员_Python类中包含一个特殊的变量( ),它表示当前对象自身,可以访问类的成员....
  • vscode 主题颜色记录
  • nRF52笔记(10)官网学习网址
  • TUV - TUV NORD - 南德 - 之间关系
  • 嵌入式--Flash芯片--NAND NOR的差别
  • NORD SK 132 M/4 BRE100 FHL
  • NORD SK672.1-112MP/4 BRE60 TF F
  • 一加 Nord 2 5G 和 OnePlus Buds Pro 齐登场
  • 知名Nord虚拟提供商遭黑客入侵,并暴露内部私钥
  • 一加OnePlus Nord预告图和相机结构图曝光
  • McGan: Mean and Covariance Feature Matching GAN
  • NORDFLASH与NANDFLASH的区别及选型
  • 时空预测文章阅读2

依赖包装了还是提示不存在_为什么要(通常)包装您的依赖关系相关推荐

  1. ASP.NET Core依赖注入最佳实践,提示技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  2. aptitude安装出现依赖_开发函数计算的正确姿势——依赖安装方法一览

    1. 前言 首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函 ...

  3. golang 依赖管理_简介:如何管理Golang项目依赖项

    golang 依赖管理 by Ying Kit Yuen 英杰苑 简介:如何管理Golang项目依赖项 (An intro to dep: How to manage your Golang proj ...

  4. 【Spring依赖循环】提前曝光,直接曝光到二级缓存已经可以解决循环依赖问题了,为什么一定要三级缓存?

    前言 问:什么是循环依赖? 循环依赖:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用. 问:Spring 如何解决循环依赖? 答:Spring 通过提前曝光机 ...

  5. leetcode c程序总提示主函数_帅气中国小哥出“大招”,程序员跳槽面试刷题必备...

    整理 | 一一 出品 | AI科技大本营 春节刚过,年终奖收入囊中,属于工程师们一年一度的跳槽季也来了. 跳槽后薪水翻倍自然爽歪歪,但最怕的是面试翻车,那就悲剧了.可想而知,想要跳槽或者为春招准备的毕 ...

  6. python wx提示框字体_使用wxStyledTextCtrl实现代码提示

    wxStyledTextCtrl是wxPython对流行的Scintilla的包装,Scintilla的网站(http://www.scintilla.org/), wxStyledTextCtrl是 ...

  7. 2018年冷链百强_在分析了47,251个依赖关系之后,2016年Java图书馆百强

    2018年冷链百强 谁在上面,谁在后面? 我们分析了Github上的47,251个依赖关系,并抽取了前100个Java库 对于长周末而言,我们最喜欢的消遣是浏览Github并搜索流行的Java库. 我 ...

  8. 计算机应用基础操作题提示,计算机应用基础_操作题文字提示(已经放大了请不要打印).doc...

    计算机应用基础_操作题文字提示(已经放大了请不要打印),苹果忘记安全提示问题,app忘记安全提示问题,app提示问题忘记,苹果提示问题忘记,安全提示问题,提示安全证书有问题,ipad忘记安全提示问题, ...

  9. springboot导入项目依赖报错_如何解决spring boot 项目导入依赖后代码报错问题

    如何解决spring boot 项目导入依赖后代码报错问题 2020-08-15  14:17:18 代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片) ​ 针对图中所示的 ...

最新文章

  1. python学习方向-Python方向(转载)
  2. 【科技金融】互联网金融简介
  3. React div加载背景图
  4. Vue组件间通信:一个例子学会Vue组件-Vue.js学习总结)(转载)
  5. 漫谈 Gentoo 中文社区的建设
  6. Java ==和Equals方法的比较
  7. 开源网络爬虫程序(spider)一览
  8. 《Redis视频教程》(p9)
  9. iOS--HealthKit简单使用
  10. 【CSS】writing-mode实现古诗词排版
  11. 微信小程序推广方式有哪些?
  12. 苹果12系列不附赠耳机充电器引争议,你还会买吗?
  13. 迅雷7 down.php,迅雷云点播放器(KCPlayer5000)
  14. 安装多可预览控件后,不能正常预览和修改该怎么办?
  15. Alevin——虚拟网络仿真平台
  16. 利用Matlab判断某些点是否在多边形区域内
  17. linux mv工作原理,linux 中mv命令
  18. 计算机组成原理——中央处理器-数据通路(课程笔记)
  19. C语言qsort的使用方法
  20. c语言指针指向怎么指,C语言-基础教程-指向指针的指针

热门文章

  1. 霍金逝世一周年,英国皇家铸币厂发售“黑洞”纪念币
  2. 魔族猎人java_王者荣耀:兰陵王新皮肤驯魔猎人曝光,80级战令皮肤!
  3. WPF关于阿里巴巴矢量图标使用细节
  4. Python运算符与表达式
  5. 微信小程序自定义tabbar导航栏,中间凸出样式
  6. VNC Linux 远程桌面控制软件
  7. jmeter中beanshell的使用
  8. SD卡引脚 电路图及工作原理介绍
  9. PCIE结构拓扑(RC、EP、SWITCH)介绍
  10. 2006世界杯32强人体彩绘队服样式(多哥)