警告: 检测到依赖关系环:

Dependencies in Software Design

软件设计的依存关系

In software development, the idea of dependencies is an issue of some importance. This article seeks to explain what dependencies are and where they can create a problem for growth or an opportunity for excellence.

在软件开发中,依赖的想法是一个重要的问题。 本文旨在解释什么是依赖关系,以及它们在哪些方面会带来增长问题或卓越机会。

In a nutshell, if program "A" calls upon program "B" for some data or service, program "A" is said to have a dependency on program "B." If the interface that program "A" uses to call program "B" changes, then program "A" must be changed, too. This sort of situation may be easy to fix

简而言之,如果程序“ A”为某些数据或服务调用程序“ B”,则称程序“ A”对程序“ B”具有依赖性。 如果程序“ A”用来调用程序“ B”的接口发生了变化,那么程序“ A”也必须被更改。 这种情况可能很容易解决

if it is an isolated case (perhaps all calls to program "B" are routed through a particular class method in program "A"). However if the coupling is not an isolated interface, the dependency on program "B" can be a real risk to the continued health of program "A."如果是孤立情况 (也许对程序“ B”的所有调用都通过程序“ A”中的特定类方法进行路由)。 但是,如果耦合不是隔离的接口,则对程序“ B”的依赖可能会严重威胁程序“ A”的持续运行。

To try to illustrate this in practical terms, let's consider an issue that many PHP programmers are facing right now. They developed web sites or web applications that are dependent on the familiar but obsolete MySQL extension. Throughout their scripts, we find numerous instances of mysql_connect(), mysql_query(), mysql_fetch_assoc(), etc. There are red warning labels on all of those main pages! It has been a couple of years since PHP deprecated the MySQL extension, and these function calls have begun to throw Warning messages. There is not any "good fix" for this problem -- the only solution is the awkward and time-consuming process of manually remediating the PHP scripts, an issue we tackled in this article. These PHP scripts have a tightly coupled dependency on MySQL, and it's not a good thing!

为了尝试用实际的方式说明这一点,让我们考虑许多PHP程序员当前面临的问题。 他们开发了依赖于熟悉但过时MySQL扩展的网站或Web应用程序。 在整个脚本中,我们发现了许多mysql_connect(),mysql_query(),mysql_fetch_assoc()等实例。所有这些主页上都有红色警告标签! 自PHP弃用MySQL扩展以来已经有两年了,这些函数调用已经开始发出警告消息。 这个问题没有任何“好的解决方案”,唯一的解决方案是手动修复PHP脚本的笨拙且耗时的过程,这是我们在本文中解决的问题。 这些PHP脚本对MySQL有着紧密耦合的依赖性,这不是一件好事!

Abstraction Layers to Isolate Dependencies

抽象层隔离依赖关系

Some developers, anticipating this problem, created database abstraction layers. Instead of calling the PHP MySQL functions directly, they built their PHP code with an interface that put a database wrapper class around the MySQL functions. The underlying database extension was still MySQL, but instead of calling the MySQL functions directly in the "whole cloth" of the web pages and applications, the web pages and applications called specialized functions in the abstraction layer. This has huge benefits for the programmers who must update PHP scripts to remove the dependency on obsolete MySQL functions, because all of the calls to the obsolete functions are contained in one place. They simply have to fix the abstraction layer and the entire web site or application is automatically modernized. Easy!

一些开发人员在预料到此问题后会创建数据库抽象层。 他们没有直接调用PHP MySQL函数,而是使用一个接口构建了自己PHP代码,该接口将数据库包装器类放在MySQL函数周围。 底层的数据库扩展仍然是MySQL,但是与其直接在网页和应用程序的“整体”中调用MySQL函数,这些网页和应用程序在抽象层中称为专用函数。 对于必须更新PHP脚本以消除对过时MySQL函数的依赖的程序员而言,这具有巨大的好处,因为对过时的函数的所有调用都包含在一个地方。 他们只需要修复抽象层,整个网站或应用程序就会自动更新。 简单!

The abstraction layer is a good step in the right direction, and it makes for easier updates but in a way, it only hides the dependency, moving it away from the application code. The dependency is still there; it's just hidden from view by a static abstraction layer. By "static" we mean that the abstraction layer is a fixed entity. It hides the dependency, and this makes for cleaner code, but by itself it can't change or remove the dependency.

抽象层是朝正确方向迈出的良好一步,它使更新更容易,但在某种程度上,它仅隐藏了依赖项,将其从应用程序代码中移开了。 依存关系仍然存在; 它只是被静态抽象层从视图中隐藏了。 “静态”是指抽象层是固定实体。 它隐藏了依赖关系,这使代码更简洁,但是就其本身而言,它无法更改或删除依赖关系。

Here is a somewhat contrived code example of a static abstraction layer, in the form of the WeatherInformationBoston class. In this example we have an application that gets the Boston weather from a service. The WeatherInformationBoston class "hides" the implementation details from the rest of the program, but it's only able to get the weather for Boston, because the URL of the weather service is hard-wired into the class. (I know you would never do something like this, but it's a contrived example intentionally made wrong to illustrate a point).

这是一个静态抽象层的精心设计的代码示例,形式为WeatherInformationBoston类。 在此示例中,我们有一个从服务中获取波士顿天气的应用程序。 WeatherInformationBoston类从程序的其余部分“隐藏”实现细节,但是由于天气服务的URL硬连接到该类中,因此它只能获取波士顿的天气。 (我知道您将永远不会做这样的事情,但这是一个故意制造的错误示例,以说明问题)。

<?php // weather_information_boston.php
error_reporting(E_ALL);/*** A Class that gets current weather information from a data source*/
Class WeatherInformationBoston
{// THE GETTER METHODS THAT MIGHT BE REQUIRED BY AN INTERFACEpublic function getTempF(){return $this->fahrenheit;}public function getTempC(){return $this->centigrade;}// THE CONSTRUCTOR SETS UP THE PATH TO THE DEPENDENCYpublic function __construct(){$this->getCurrentWeather('http://iconoun.com/demo/weather_service_boston.php');}// THE ACCESSOR METHOD USES THE PATH TO THE DATApublic function getCurrentWeather($url){// READ AND INTERPRET THE WEATHER INFORMATION$jso = file_get_contents($url);$obj = json_decode($jso);// SET THE OBJECT PROPERTIES FROM THE EXTERNAL DATA SOURCE$this->fahrenheit = $obj->fahrenheit;$this->centigrade = $obj->centigrade;}
}$weatherObject = new WeatherInformationBoston();// ACCESS THE WEATHER INFORMATION
$f = $weatherObject->getTempF();
$c = $weatherObject->getTempC();// PREPARE A REPORT
$weather_report = <<<EOD
In Boston, the temperature is: $f&deg;F ($c&deg;C)
EOD;echo $weather_report;

If we also needed to get the weather for Miami, we would need to set up another class, maybe WeatherInformationMiami. If we needed to get the weather for hundreds of cities we would need hundreds of classes. And if we had a change in the weather services that provided our data we would need to change hundreds of scripts. Just like the painful remediation of MySQL, this is a difficult and time-consuming problem with no good solution. The only way to handle this problem is to anticipate the problem and use a design pattern that allows us to avoid the problem completely.

如果还需要获取迈阿密的天气,则需要设置另一个类,也许是WeatherInformationMiami。 如果我们需要获取数百个城市的天气,则需要数百个课程。 而且,如果我们更改了提供数据的气象服务,我们将需要更改数百个脚本。 就像MySQL的痛苦修复一样,这是一个困难且耗时的问题,没有好的解决方案。 解决此问题的唯一方法是预见问题,并使用允许我们完全避免出现问题的设计模式。

Dependency Injection

依赖注入

Dependency Injection ("DI") is the term given to the real-time resolution of programmatic dependencies. It has all of the benefits of static abstraction layers, but it provides us with a dynamically created abstraction layer. The implementation details of the abstraction layer are created at run-time and the dependencies can be injected into the application. There are three main types of dependency injection: Constructor injection, Setter injection and Interface injection (see Further Reading, below). The implementation details differ, but they all share the ability to allow a run-time resolution of the dependencies, and they all avoid the same problem of binding your code to a hard-wired dependency. In our example below, we modify the WeatherInformationBoston class to generalize the class so that it can use other weather services and thereby return weather information from other cities.

依赖注入(DI)是赋予程序依赖的实时解决方案的术语。 它具有静态抽象层的所有优点,但为我们提供了动态创建的抽象层。 抽象层的实现细节是在运行时创建的,并且可以将依赖项注入到应用程序中。 依赖项注入有三种主要类型:构造函数注入,Setter注入和接口注入(请参见下面的进一步阅读)。 实现细节有所不同,但是它们都具有允许在运行时解析依赖项的功能,并且都避免了将代码绑定到硬性依赖项的相同问题。 在下面的示例中,我们修改WeatherInformationBoston类以将其通用化,以便它可以使用其他天气服务,从而从其他城市返回天气信息。

<?php // weather_information.php
error_reporting(E_ALL);/*** A Class that gets current weather information from a data source*/
Class WeatherInformation
{// THE GETTER METHODS THAT MIGHT BE REQUIRED BY AN INTERFACEpublic function getTempF(){return $this->fahrenheit;}public function getTempC(){return $this->centigrade;}public function getDataSource(){return $this->dataSource;}// THE CONSTRUCTOR SETS UP THE PATH TO THE DEPENDENCYpublic function __construct($url){$this->getCurrentWeather($url);}// THE ACCESSOR METHOD USES THE PATH TO THE DATApublic function getCurrentWeather($url){// READ AND INTERPRET THE WEATHER INFORMATION$jso = file_get_contents($url);$obj = json_decode($jso);// SET THE OBJECT PROPERTIES FROM THE EXTERNAL DATA SOURCE$this->dataSource = $obj->dataSource;$this->fahrenheit = $obj->fahrenheit;$this->centigrade = $obj->centigrade;}
}/*** Constructor Injection Sets the URL path to the weather data*/
$src = 'http://iconoun.com/demo/weather_service_miami.php';
$weatherObject = new WeatherInformation($src);// ACCESS THE WEATHER INFORMATION
$d = $weatherObject->getDataSource();
$f = $weatherObject->getTempF();
$c = $weatherObject->getTempC();// PREPARE A REPORT
$weather_report = <<<EOD
In $d, the temperature is: $f&deg;F ($c&deg;C)
EOD;echo $weather_report;

Since we can now inject our dependencies, we can change the underlying code without any changes to our application. We simply modify the way we call the WeatherInformation class constructor (our bootstrap script), and a new dependency can be substituted. In our example, the new dependency is represented by the URL of the weather service that returns our data. As other weather services become available, we can add more cities to our application.

由于我们现在可以注入依赖项,因此可以更改基础代码,而无需对应用程序进行任何更改。 我们只需修改调用WeatherInformation类构造函数(我们的引导脚本)的方式,就可以替换新的依赖项。 在我们的示例中,新的依存关系由返回我们的数据的气象服务的URL表示。 随着其他天气服务的推出,我们可以在应用程序中添加更多城市。

Dependency Injection in Automated Testing

自动化测试中的依赖注入

A few years ago I wrote an article on "test-driven development."  In a nutshell, test-driven development, or "TDD" for short, is a work process that guides you to the best possible programming outcomes. You test your programming by running repeated incremental tests as you build your software. The test cases mirror the likely data sets you would encounter in the deployed environment (and some of the edge-cases).  If your software can produce predictable output for each of the test cases - and if the output is what you want - you have greatly increased the likelihood that your software deployment will succeed once it is released into the wild.

几年前,我写了一篇关于“测试驱动开发”的文章。 简而言之,测试驱动开发或简称为“ TDD”是一个工作过程,可指导您获得最佳的编程结果。 在构建软件时,可以通过运行重复的增量测试来测试编程。 测试用例反映了您在部署的环境中可能遇到的数据集(以及某些边缘情况)。 如果您的软件可以为每个测试用例产生可预测的输出-并且如果输出是您想要的-您就可以大大提高一旦将软件发布到野外就可以成功部署软件的可能性。

If you're smart about your TDD, you will choose automated tests. Of course you could just look at the inputs and outputs to see if they line up in a sensible way, but that approach is kind of limiting, and requires you to be engaged in a dynamic and interactive man-machine dialog for every test. Instead, we want to write automation that feeds our test data to our system under test, and checks the return values automatically. If everything works well, we can put out a "green means go" happy message. If some of the tests fail to produce the expected output, we can put out a "red means stop" message and highlight the tests that failed our expectations. This is the design that phpunit follows, and it has become one of the dominant patterns in the world of automated testing.

如果您对TDD有所了解,则可以选择自动测试。 当然,您可以只查看输入和输出,以查看它们是否以合理的方式排列,但是这种方法是一种限制,并且要求您在每次测试时都进行动态的交互式人机对话。 相反,我们希望编写自动化程序,将我们的测试数据馈送到被测系统,并自动检查返回值。 如果一切正常,我们可以发出“绿色意味着走”的快乐信息。 如果某些测试未能产生预期的输出,我们可以发出“红色表示停止”消息,并突出显示未能达到我们预期的测试。 这是phpunit遵循的设计,它已成为自动化测试领域的主要模式之一。

Phpunit creates an environment of "mock objects," where programmatic constructs are either real (meaning that they are actual instances of your code and external services) or mocked instances, given the task of standing in and given the responsibility of receiving predictable inputs and producing predictable outputs. Think of these mock objects as the crash-test-dummies of your application. If you design your tests correctly you can inject a mock object for every dependency of your code, then remove the mocks one-at-a-time, rerunning the tests each time a mock is removed and your actual code is employed. This can create a testing environment where code coverage is at or near 100%. In other words, you can test every single object through the entire application, and verify that each object works correctly.

Phpunit创建了一个“模拟对象”环境,其中的编程构造是真实的(意味着它们是您的代码和外部服务的实际实例)或模拟的实例,给定了站立的任务并承担了接收可预测的输入并产生的责任。可预测的输出。 可以将这些模拟对象视为应用程序的崩溃测试假人。 如果您正确地设计测试,则可以为代码的每个依赖项注入一个模拟对象,然后一次删除该模拟,并在每次删除模拟并使用实际代码时重新运行测试。 这可以创建代码覆盖率达到或接近100%的测试环境。 换句话说,您可以在整个应用程序中测试每个对象,并验证每个对象是否正常工作。

Phpunit requires considerable setup and background understanding that is beyond the scope of this article, but we can show an example of using an automated test to verify that our code is returning the expected values. Here is a modification of the weather information script with an automated test attached at the end. Try running it as-is, then try modifying the expectation to see what happens. Or try modifying the URL path to the weather data. Well-constructed tests like these will save you many, many hours of development time!

Phpunit需要大量的设置和背景知识,这超出了本文的范围,但是我们可以展示一个示例,该示例使用自动化测试来验证我们的代码是否返回了期望值。 这是对天气信息脚本的修改,最后附加了自动测试。 尝试按原样运行它,然后尝试修改期望值以查看会发生什么。 或尝试修改天气数据的URL路径。 诸如此类的结构良好的测试将为您节省很多很多的开发时间!

<?php // weather_information_test.php
error_reporting(E_ALL);/*** A Class that gets current weather information from a data source*/
Class WeatherInformation
{// THE GETTER METHODS THAT MIGHT BE REQUIRED BY AN INTERFACEpublic function getTempF(){return $this->fahrenheit;}public function getTempC(){return $this->centigrade;}public function getDataSource(){return $this->dataSource;}// THE CONSTRUCTOR SETS UP THE PATH TO THE DEPENDENCYpublic function __construct($url){$this->getCurrentWeather($url);}// THE ACCESSOR METHOD USES THE PATH TO THE DATApublic function getCurrentWeather($url){// READ AND INTERPRET THE WEATHER INFORMATION$jso = file_get_contents($url);$obj = json_decode($jso);// SET THE OBJECT PROPERTIES FROM THE EXTERNAL DATA SOURCE$this->dataSource = $obj->dataSource;$this->fahrenheit = $obj->fahrenheit;$this->centigrade = $obj->centigrade;}
}/*** Constructor Injection Sets the URL path to the weather data*/
$src = 'http://iconoun.com/demo/weather_service_miami.php';
$weatherObject = new WeatherInformation($src);// ACCESS THE WEATHER INFORMATION
$d = $weatherObject->getDataSource();
$f = $weatherObject->getTempF();
$c = $weatherObject->getTempC();// PREPARE A REPORT
$weather_report = <<<EOD
In $d, the temperature is: $f&deg;F ($c&deg;C)
EOD;/*** Test the data against our expectations*/
$expected_report = 'In Miami, the temperature is: 98.6&deg;F (37&deg;C)';
if ($weather_report == $expected_report)
{echo PHP_EOL . '<span style="color:green;">Success!</span>' . PHP_EOL;
}
else
{echo PHP_EOL . '<span style="color:red;">Failure Asserting that Expected ' . $expected_report . ' == Actual ' . $weather_report . '</span>' . PHP_EOL;
}

How DI Facilitates Automated Testing

DI如何促进自动化测试

If you cannot do dependency injection, you are very limited in how you can test your scripts. Consider the procedural MySQL example. Everything in the script depends on MySQL. The dependency is hard-wired into the code in many different places. There is no way to get to each of these hard-wired places with mock examples of MySQL. If you take MySQL out of one part of the application, you take it out of all of the places in the application, thus the application is untestable. And if an application cannot be tested, you cannot know whether it truly meets the requirements. Similarly, if we cannot test, we cannot verify that changes in our implementation of new requirements have not introduced errors in other parts of the application. Fix one, break two - that's never a happy path.

如果无法进行依赖项注入,则测试脚本的方式将非常有限。 考虑一下过程式MySQL示例。 脚本中的所有内容均取决于MySQL。 依赖关系在很多地方硬连接到代码中。 没有办法通过MySQL的模拟示例来访问这些固定的地方。 如果将MySQL从应用程序的一部分中删除,则会将其从应用程序的所有位置中删除,因此该应用程序不可测试。 而且,如果无法测试某个应用程序,您将无法知道它是否真正满足要求。 同样,如果我们无法测试,就无法验证我们对新要求的实现所做的更改没有在应用程序的其他部分引入错误。 解决一个,打破两个-这绝不是一条快乐的路。

But if we have object-oriented MySQL and we inject the MySQL object into our classes, we can selectively mock the MySQL object in each of the classes. It can be the "real" database in some parts of the application and a mock object in other parts. This means we have great control over the way MySQL responds to our tests, giving us a fine granularity of code assessment, and enabling us to prove that our code works correctly. Tools like phpunit use dependency injection to insert and remove mock objects in real time, allowing rapid repeated tests.

但是,如果我们具有面向对象MySQL,并将MySQL对象注入到我们的类中,则可以在每个类中选择性地模拟MySQL对象。 它可以是应用程序某些部分的“真实”数据库,而在其他部分则可以是模拟对象。 这意味着我们可以很好地控制MySQL响应测试的方式,从而为我们提供了精细的代码评估粒度,并使我们能够证明我们的代码可以正常工作。 phpunit之类的工具使用依赖注入实时插入和删除模拟对象,从而允许快速重复测试。

How easy is it to test with mock objects and DI?  In an application I'm currently developing for the Department of Defense, we run unit tests each time we make even a one-line change to the code set. There are hundreds of tests in the suite, usually

使用模拟对象和DI进行测试有多容易? 在我当前为国防部开发的应用程序中,每次对代码集进行单行更改时,我们都会运行单元测试。 套件中通常有数百个测试

每个类方法at least two tests for each class method (one with expected input and success, and one with invalid input and failure) but sometimes there are more tests, if a range of inputs or outputs is in play. The entire application can be tested, 至少要进行两次测试 (一项具有预期的输入和成功,而一项具有无效的输入和失败),但如果有一系列的输入或输出在起作用,则有时会有更多的测试。 可以在不到10秒的时间内对整个应用程序进行with 100% code coverage, in less than 10 seconds. This sort of exhaustive testing would be impossible with hard-wired dependencies. 100%代码覆盖率的测试。 对于硬性依赖关系,这种详尽的测试是不可能的。

Summary

摘要

This article has explained the difference between built-in dependencies, abstraction layers, and dependency injection. We have shown ways to make our code more flexible and more testable.  Using design patterns like these we can build code that is highly dependable and easy to reuse in other applications.

本文介绍了内置依赖项,抽象层和依赖项注入之间的区别。 我们已经展示了使我们的代码更灵活,更可测试的方法。 使用这样的设计模式,我们可以构建高度可靠且易于在其他应用程序中重用的代码。

Addendum

附录

Here are the "weather service" scripts that are used as dependencies in the code examples above.

这是上面的代码示例中用作依赖项的“天气服务”脚本。

<?php // weather_service_boston.php
error_reporting(E_ALL);$data = new StdClass;
$data->dataSource = 'Boston';
$data->fahrenheit = 50;
$data->centigrade = 10;echo json_encode($data);
<?php // weather_service_miami.php
error_reporting(E_ALL);$data = new StdClass;
$data->dataSource = 'Miami';
$data->fahrenheit = 98.6;
$data->centigrade = 37;echo json_encode($data);

Further Reading (other opinions, etc.)

进一步阅读 (其他意见等)

http://fabien.potencier.org/article/11/what-is-dependency-injection

http://fabien.potencier.org/article/11/what-is-dependency-injection

http://code.tutsplus.com/tutorials/dependency-injection-in-php--net-28146

http://code.tutsplus.com/tutorials/dependency-injection-in-php--net-28146

http://martinfowler.com/articles/injection.html

http://martinfowler.com/articles/injection.html

https://r.je/constructor-injection-vs-setter-injection.html

https://r.je/constructor-injection-vs-setter-injection.html

http://www.sitepoint.com/dependency-injection-with-pimple/

http://www.sitepoint.com/dependency-injection-with-pimple/

http://symfony.com/doc/current/components/dependency_injection/types.html

http://symfony.com/doc/current/components/dependency_injection/types.html

http://www.whitewashing.de/2008/09/20/dependency-injection-via-interface-in-php-an-example.html

http://www.whitewashing.de/2008/09/20/dependency-injection-via-interface-in-php-an-example.html

http://www.sitepoint.com/dependency-injection-laravels-ioc/

http://www.sitepoint.com/dependency-injection-laravels-ioc/

http://culttt.com/2014/03/24/exploring-laravels-ioc-container/

http://culttt.com/2014/03/24/exploring-laravels-ioc-container/

Please give us your feedback!

请给我们您的反馈意见!

If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!

如果您发现本文有帮助,请单击下面的“竖起大拇指”按钮。 这样做可以使EE社区了解对EE成员有价值的内容,并为将来的文章提供指导。 如果您有任何问题或意见,请添加。 谢谢!

翻译自: https://www.experts-exchange.com/articles/18210/Software-Design-Dependencies.html

警告: 检测到依赖关系环:

警告: 检测到依赖关系环:_软件设计:依赖关系相关推荐

  1. 极客时间_软件设计之美 笔记

    极客时间_软件设计之美 笔记 1.软件设计是什么 写代码前应该有模型设计 比如mvc结构就是一种模型 代码应该有规范, 产品内部原则 避免每个开发按自己的想法实现,有人写rest 有人写mvc 代码模 ...

  2. java 工厂模式_软件设计工厂模式使用场景介绍及java举例(附源码)

    工厂模式是软件设计中最常用也最常见的一种模式,这里通过一个例子来说明其功能. 问题背景 用户需求:请提供 1).图形绘制功能,当前可绘制当前可绘制原型.方形.三角形.未来可能需要支持绘制菱形.梯形等形 ...

  3. java mysql 表关系分析_数据库表的关系

    表与表之间一般存在三种关系,即一对一,一对多,多对多关系. 下面分别就三种关系讲解数据库相关设计的思路和思考过程: (1)一对一关系 例如,下面的一张表,保存了人的相关信息,有男有女,要求查处所有的夫 ...

  4. java游戏界面制作_软件设计之基于Java的连连看小游戏(二)——游戏基础界面的制作及事件的添加...

    上次完成到游戏首页的制作,今天完成了游戏基础界面的制作以及事件的简单添加.由于功能尚未完全实现,因此游戏界面的菜单列表只是简单地添加了一下,其余菜单列表以及倒计时等在后续的制作中逐一完善. 1.首先在 ...

  5. mysql的关系图_学会sql数据库关系图

    很久以前就知道微软的Petshop的很经典,昨天抽出时间去学习,一开始还真的不适应,什么成员资格,还真的看不太懂,运行petshop想从登陆学起,但是用户名和密码都不知道,后来发现有更注册的页面,自己 ...

  6. idea导入maven项目依赖报错_解决Maven依赖冲突的好帮手,这款IDEA插件了解一下?

    1.何为依赖冲突 Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的.Maven的依赖机制会导致Jar包的冲突. 举个例子,现在你的项目中,使用了两个Jar包,分别是A和B.现在A需要依赖 ...

  7. 网页制作和java有关系么_网页设计注意问题

    网页设计注意问题 引导语:网页设计时应考虑哪些方面的问题,包括网站功能和以及访问者需要什么.你的整个设计都应该围绕这些方面来进行.以下是小编整理的网页设计注意问题,欢迎参考阅读! 1.页面内容要新颖 ...

  8. python画父子关系图_将有父子关系的一维数组转换成树形结构(多维)数据

    先来个函数注释 : /** * 将有父子关系的一维数组转换成树形结构(多维)数据 * console.log(JSON.stringify(setTreeData(data), null, 2)); ...

  9. echarts做企业关系图谱_建立良好客户关系 做有温度的企业

    客户关系管理(Customer Relationship Management)可以说是企业发展的重大课题.聪明的企业会从客户及潜在客户身上挖掘有用的信息,再将其转化为有助业务的数据.此外,妥善管理客 ...

最新文章

  1. 鸿蒙程序如何上传,鸿蒙应用程序流转的实现
  2. JS阻止事件冒泡的3种方法,以及他们之间的不同
  3. 鸿蒙os即将升级,央视爆料鸿蒙OS即将升级,荣耀智慧屏强大自研开启国货新时代...
  4. 一件代发系统php网站源码_靠谱礼品代发平台网站一件代发(0.5元)
  5. mail发送的内容显示为附件的解决办法
  6. python flask框架是什么_Flask框架是什么?带你安装运行第一个Flask程序
  7. 基于JAVA+SpringMVC+MYSQL的进销存管理系统
  8. 在DC中误删除ISA计算机后无法连接ISA配置服务器问题
  9. java找三个数最大_用Java程序找最大的数字(4)
  10. JavaWeb:Maven
  11. cv2中函数名的规则
  12. 浅析机关单位人力资源内部控制
  13. python数据分析与可视化答案学堂云_智慧树知到_Python数据分析与数据可视化_最新答案...
  14. 阿里 卫哲谈阿里人力招聘价值观
  15. 毕业三到五年,别让“努力”毁了你--唯有提升个人实力才是王道
  16. Win10电脑很卡反应很慢该如何处理
  17. MySQL sum()函数
  18. windows中mysql服务无法启动
  19. iOS 添加在scrollview的子控件,用masonry布局的问题
  20. 我是如何治愈自己的悲观,抑郁,胡思乱想,优柔寡断的

热门文章

  1. eclipes使用方法
  2. 一元三次方程的求解(二分法)
  3. surface怎么将计算机放到桌面,笔者帮您win10系统把此电脑和控制面板在桌面上显示的妙计...
  4. 网上打印文件怎么发给商家?怎么给商家发送打印资料
  5. 华为电脑计算机怎么显示在桌面,电脑桌面小便签,华为电脑怎么设置桌面便签...
  6. 实现手机浏览器跳转微信关注公众号
  7. 动态规划------求解决策过程中最优化的数学方法
  8. xlsx 导出导入excel,xlsx-style 修改excel样式
  9. 闲云野鹤:吃鸡(二)之场景制作—雾效的制作
  10. python数据处理工具-Pandas笔记