热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

十六、添加依赖注入容器

十六、添加依赖注入容器我们已经到了现代化进程的最后一步。我们将通过将页面脚本的剩余逻辑移动到依赖

十六、添加依赖注入容器

我们已经到了现代化进程的最后一步。我们将通过将页面脚本的剩余逻辑移动到依赖项注入容器中,来移除页面脚本的最后一部分。容器将负责协调应用程序中的所有对象创建活动。在此过程中,我们将再次修改前端控制器,并开始添加指向控制器类而不是文件路径的路由。

对于现代化过程的最后一步,最好安装 PHP5.3 或更高版本。这是因为应用程序逻辑的关键部分需要闭包。如果我们不能访问 PHP5.3,那么有一个不太可行但仍然可行的选项来实现依赖注入容器。我们将这一情况作为本章最后一个“共同问题”来处理。

什么是依赖注入容器?

依赖注入作为一种技术,我们从本书早期开始就一直在实践。重申一下,依赖注入背后的思想是我们从外部将依赖项推送到对象中。这与通过 new 关键字在类内部创建依赖项对象相反,或者通过globals关键字超出当前范围引入依赖项。

关于控制反转和依赖注入的概述,请阅读 Fowler 关于容器的文章http://martinfowler.com/articles/injection.html 。

为了完成依赖项注入活动,我们已经在页面脚本中手动创建了必要的对象。对于任何需要依赖项的对象,我们首先创建依赖项,然后创建依赖它的对象并传入依赖项。这个创建过程有时是深层次的,就像依赖项有依赖项一样。不管复杂性和深度如何,这样做的逻辑目前嵌入在页面脚本中。

依赖注入容器背后的想法是将所有对象创建逻辑保存在一个地方,这样我们就不再需要使用页面脚本来设置对象。我们可以将容器中的每个对象创建逻辑放在一个唯一的名称下,称为服务。

然后,我们可以告诉容器返回任何已定义服务对象的新实例。或者,我们可以告诉容器创建并返回该服务对象的共享实例,这样每次我们得到它时,它总是同一个实例。仔细组合容器服务的新实例和共享实例将允许我们减少依赖项创建逻辑。

在任何时候,我们都不会将容器传递到任何需要依赖关系的对象中。要做到这一点,需要使用一种称为服务定位器的模式。我们避免服务定位器活动,因为这样做违反了范围。当容器位于一个对象内部,并且该对象使用它来检索依赖项时,我们只需要从开始的地方走一步;也就是说,使用global关键字。因此,我们不传递容器——它完全位于它创建的对象的范围之外。

PHPLAND 中有许多不同的容器实现,每个都有自己的优缺点。为了使事情适合我们的现代化进程,我们将使用Mlaphp\Di。这是一个精简的容器实现,非常适合我们的过渡需要。

添加 DI 容器

添加 DI 容器的流程一般如下:


  1. 添加一个新的services.php包含文件以创建容器并管理其服务。

  2. 在容器中定义一个router服务。

  3. 修改前端控制器以包含services.php文件并使用router服务,然后抽查应用。

  4. 将创建逻辑从每个页面脚本提取到容器:

    1. 在为页面脚本控制器类命名的容器中创建服务。

    2. 将页面脚本中的逻辑复制到容器服务中。根据需要重命名变量以使用 DI 容器属性。

    3. 将页面 URL 路径路由到容器服务名称(即控制器名称)。

    4. 抽查并提交更改。

    5. 继续,直到将所有页面脚本提取到容器中。



  5. 移除空的pages/目录,提交、推送并通知 QA。


添加 DI 容器包含文件

为了防止现有的安装文件变得更大,我们将引入一个新的services.php安装文件。是的,这意味着在前端控制器中添加另一个include,但如果我们一直努力,那么在我们的应用程序中几乎没有剩余的包含项。这一个意义不大。

首先,我们需要为文件选择一个合适的位置。如果它与我们已有的任何其他安装文件一起使用,可能是在现有的includes/目录中,这可能是最好的。

然后我们用下面的行创建文件。(我们将在此文件中添加更多内容)由于该文件将作为最后一个安装文件加载,我们可以假定自动加载将处于活动状态,因此无需加载Di类文件:

includes/services.php
1 2 $di = new \Mlaphp\Di($GLOBALS);
3 ?>

结果是,新的$di实例加载了所有现有的全局变量值。这些值作为属性保留在容器上。例如,如果我们的设置文件创建了一个$db_user变量,我们现在可以额外访问该值作为$di->db_user。这些是副本,不是引用,因此对其中一个的更改不会影响另一个。

为什么我们保留现有变量作为属性?

目前,我们的页面脚本直接访问全局变量进行创建工作。但是,在后面的步骤中,创建逻辑将不再在全局范围内。它将位于 DI 容器的“内部”。因此,我们使用变量的副本填充 DI 容器,否则这些变量将可用。

添加路由器服务

既然在位置有了 DI 容器,那么让我们添加我们的第一个服务。

回想一下,DI 容器的目的是为我们创建对象。当前,前端控制器创建了一个路由器对象,因此我们将向容器中添加一个router服务。(在下一步中,我们将让前端控制器使用此服务,而不是自己创建路由器。)

services.php文件中,添加以下行:

includes/services.php
1 2 // set a container service for the router
3 $di->set('router', function () use ($di) {
4 $router = new \Mlaphp\Router('/path/to/app/pages');
5 $router->setRoutes(array());
6 return $router;
7 });
8 ?>

让我们稍微检查一下服务定义。


  • 服务名称为router。对于打算创建一次作为共享实例的服务对象,我们将使用所有小写名称;对于打算每次创建为新实例的服务对象,我们将使用完全限定的类名。因此,在这种情况下,我们的目的是通过容器只提供一个共享的router。(这是一种约定,而不是由容器强制执行的规则。)

  • 服务定义是可调用的。在这种情况下,它是一个闭包。闭包没有收到任何参数,但它确实使用了当前范围中的$di对象。这使得定义代码可以在构建服务对象时访问容器属性和其他容器服务。

  • 我们创建并返回由服务名称表示的对象。我们不需要检查对象是否已经存在于容器中;如果我们请求一个共享实例,容器内部将为我们实现这一点。

通过这段代码,容器现在知道如何创建router服务。只有在调用$di->newInstance()(获取服务对象的新实例)或$di->get()(获取服务对象的共享实例)时,才会执行延迟加载的代码。

修改前控制器

既然有了 DI 容器和router服务定义,我们修改了前端控制器来加载容器并使用router服务。

docroot/front.php
1 2 require dirname(__DIR__) . '/includes/setup.php';
3 require dirname(__DIR__) . '/includes/services.php';
4
5 // get the shared router service
6 $router = $di->get('router');
7
8 // match against the url path
9 $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
10 $route = $router->match($path);
11
12 // container service, or page script?
13 if ($di->has($route)) {
14 // create a new $controller instance
15 $cOntroller= $di->newInstance($route);
16 } else {
17 // require the page script
18 require $route;
19 }
20
21 // invoke the controller and send the response
22 $respOnse= $controller->__invoke();
23 $response->send();
24 ?>

我们对以前的实现进行了以下更改:


  • 我们为services.php容器文件添加了一个require,这是我们最后一次设置。

  • 我们不直接创建路由器对象,而是get()$di容器中创建router服务对象的共享实例。

  • 我们的调度逻辑有所改变。在我们从$router得到$route后,我们检查$di集装箱has()是否有匹配的服务。如果是,则将该$route作为新$controller实例的服务名称;否则,它会将$route视为pages/中创建$controller的文件。无论哪种方式,代码都会调用控制器并发送响应。

在这些更改之后,我们会抽查应用程序,以确保新的router服务正常工作。如果没有,我们将撤消并重做到目前为止的更改,直到应用程序像以前一样工作。

一旦应用程序运行,我们可能希望提交我们的更改。这就是说,如果将来的更改变糟,我们可以恢复到一个已知的工作状态。

将页面脚本提取到服务

现在是对传统应用程序进行现代化的最后一步。我们将逐个删除页面脚本,并将其逻辑放入容器中。

创建一个容器服务

选择任何一个页面脚本,确定它用来创建其$controller实例的类。然后,在 DI 容器中,为该类名创建一个空的服务定义。

例如,如果我们有此页面脚本:

pages/articles.php
1 2 $db = new Database($db_host, $db_user, $db_pass);
3 $articles_gateway = new ArticlesGateway($db);
4 $users_gateway = new UsersGateway($db);
5 $article_transactiOns= new ArticleTransactions(
6 $articles_gateway,
7 $users_gateway
8 );
9 $respOnse= new \Mlaphp\Response('/path/to/app/views');
10 $cOntroller= new \Controller\ArticlesPage(
11 $request,
12 $response,
13 $user,
14 $article_transactions
15 );
16 ?>

我们知道正在实例化的控制器类是Controller\ArticlesPage。在我们的services.php文件中,我们使用该名称创建了一个空的服务定义:

includes/services.php
1 2 $di->set('Controller\ArticlesPage', function () use ($di) {
3 });
4 ?>

接下来,我们将页面脚本设置逻辑移动到服务定义中。当我们这样做时,我们应该注意我们期望从全局范围中得到的任何变量,并在它们前面加上$di->以引用适当的容器属性。(回想一下,这些都是从services.php文件的早期$GLOBALS加载的。)我们还将在定义的末尾返回控制器实例。

当我们完成时,服务定义将如下所示:

includes/services.php
1 2 $di->set('Controller\ArticlesPage', function () use ($di) {
3 // replace `$variables` with `$di->` properties
4 $db = new Database($di->db_host, $di->db_user, $di->db_pass);
5 // create dependencies
6 $articles_gateway = new ArticlesGateway($db);
7 $users_gateway = new UsersGateway($db);
8 $article_transactiOns= new ArticleTransactions(
9 $articles_gateway,
10 $users_gateway
11 );
12 $respOnse= new \Mlaphp\Response('/path/to/app/views');
13 // return the new instance
14 return new \Controller\ArticlesPage(
15 $request,
16 $response,
17 $user,
18 $article_transactions
19 );
20 });
21 ?>

一旦我们将逻辑复制到容器中,我们将从pages/中删除原始页面脚本文件。

将 URL 路径路由到容器服务

现在我们已经删除了页面脚本以支持容器服务,我们需要确保路由器指向容器服务,而不是现在缺少的页面脚本。我们通过在setRoutes()方法参数中添加一个数组元素来实现这一点,其中键是 URL 路径,值是服务名称。

例如,如果 URL 路径为/articles.php并且我们的新容器服务名为Controller\ArticlesPage,我们将修改我们的router服务,如下所示:

includes/services.php
1 2 // ...
3 $di->set('router', function () use ($di) {
4 $router = new \Mlaphp\Router($di->pages_dir);
5 $router->setRoutes(array(
6 // add a route that points to a container service name
7 '/articles.php' => 'Controller\ArticlesPage',
8 ));
9 return $router;
10 });
11 ?>

抽查并提交

最后,我们检查从页面脚本到容器服务的转换是否如我们预期的那样工作。我们通过浏览或以其他方式调用该 URL 来抽查旧页面脚本的 URL 路径。如果成功,那么我们知道容器服务已经成功地取代了现在已删除的页面脚本。

如果没有,我们需要撤销和重做我们的更改,看看哪里出了问题。我在这里看到的最常见错误是:


  • 无法将页面脚本中的$var变量替换为服务定义中的$di->var属性

  • 无法从服务定义返回对象

  • 控制器服务名称与映射的路由值不匹配

一旦我们确定应用程序将 URL 路由到新的容器服务,并且该服务工作正常,我们就提交更改。

做。。。虽然

我们继续下一页脚本并重新开始该过程。当所有页面脚本都转换为容器服务并删除后,我们就完成了。

删除页面/提交、推送、通知 QA

在我们将所有页面脚本提取到 DI 容器后,pages/目录应该是空的。我们现在可以安全地移除它。

这样,我们就可以提交工作,推送到公共存储库,并通知 QA 我们有新的更改供他们审查。

常见问题


我们如何完善我们的服务定义?

当我们将对象创建逻辑提取到容器中时,每个服务定义都可能相当长,并且可能是重复的。最好减少重复并细化服务定义,使其简短明了。我们可以通过进一步将对象创建逻辑的每个部分提取到它自己的服务来实现这一点。

例如,如果我们有几个使用请求对象的服务,我们可以将对象创建逻辑提取到它自己的服务中,然后在其他服务中引用该服务。我们可以将其命名以表明我们的意图,即将其用作共享服务(request)或新实例(Mlaphp\Request)。然后,其他服务可以使用get()newInstance(),而不是在内部创建请求。

考虑到我们早期的Controller\ArticlesPage服务,我们可以将其拆分为几个可重用的服务,如下所示:

includes/services.php
1 2 // ...
3
4 $di->set('request', function () use ($di) {
5 return new \Mlaphp\Request($GLOBALS);
6 });
7
8 $di->set('response', function () use ($di) {
9 return new \Mlaphp\Response('/path/to/app/views');
10 });
11
12 $di->set('database', function () use ($di) {
13 return new \Database(
14 $di->db_host,
15 $di->db_user,
16 $di->db_pass
17 );
18 });
19
20 $di->set('Domain\Articles\ArticlesGateway', function () use ($di) {
21 return new \Domain\Articles\ArticlesGateway($di->get('database'));
22 });
23
24 $di->set('Domain\Users\UsersGateway', function () use ($di) {
25 return new \Domain\Users\UsersGateway($di->get('database'));
26 });
27
28 $di->set('Domain\Articles\ArticleTransactions', function () use ($di) {
29 return new \Domain\Articles\ArticleTransactions(
30 $di->newInstance('Domain\Articles\ArticlesGateway'),
31 $di->newInstance('Domain\Users\UsersGateway'),
32 );
33 });
34
35 $di->set('Controller\ArticlesPage', function () use ($di) {
36 return new \Controller\ArticlesPage(
37 $di->get('request'),
38 $di->get('response'),
39 $di->user,
40 $di->newInstance('Domain\Articles\ArticleTransactions')
41 );
42 });
43 ?>

注意服务现在如何引用容器中的其他服务来构建自己的对象。当我们获得Controller\ArticlesPage服务对象的新实例时,它会寻址$di容器,以获得共享请求和响应对象、$user属性以及ArticleTransactions服务对象的新实例。反过来,它递归地寻址$di容器以获取该服务对象的依赖项,依此类推。

如果页面脚本中有 include 怎么办?

尽管我们已经尽了最大努力删除它们,但我们的页面脚本中仍可能有一些包含文件。当我们将页面脚本逻辑复制到容器中时,除了复制它们之外,我们别无选择。但是,一旦所有页面脚本都转换为容器,我们就可以寻找共性,并开始将包含逻辑提取到设置脚本或单独的类中(如果需要,这些类本身可以成为服务)。

我们可以减小 services.php 文件的大小吗?

根据应用程序中页面脚本的数量,我们的 DI 容器最终可能会有数十个或数百个服务定义。在一个文件中可以管理或扫描很多内容。

如果我们愿意,将容器拆分为多个文件,并进行一系列 include 调用以引入各种定义是完全合理的。

我们可以减少路由器服务的规模吗?

作为 DI 容器文件长度的子集,特别是router服务可能变得非常长。这是因为我们将应用程序中的每个 URL 映射到一个服务;如果有数百个 URL,那么将有数百条router行。

或者,我们可以创建一个单独的routes.php文件,让它返回一个路由数组。然后我们可以在setRoutes()调用中包含该文件:

includes/routes.php
1 2 '/articles.php' => 'Controller\ArticlesPage',
3 ); ?>

includes/services.php
1 2 // ...
3 $di->set('router', function () use ($di) {
4 $router = new \Mlaphp\Router($di->pages_dir);
5 $router->setRoutes(include '/path/to/includes/routes.php');
6 return $router;
7 });
8 ?>

这至少会减少services.php文件的大小,即使它不会减少 routes 数组的大小。

如果我们不能更新到 PHP5.3 怎么办?

本章中的示例展示了使用闭包封装对象创建逻辑的 DI 容器。闭包只在 PHP5.3 中可用,所以如果我们停留在早期版本的 PHP 上,看起来使用 DI 容器根本不是一个选项。

事实证明并非如此。通过一些额外的努力和对不雅的容忍,我们仍然可以为 PHP5.2 和更早版本构建 DI 容器。

首先,我们需要扩展 DI 容器,以便向其添加方法。然后,我们不再将服务定义创建为闭包,而是将它们创建为扩展容器上的方法:

classes/Di.php
1 2 class Di extends \Mlaphp\Di
3 {
4 public function database()
5 {
6 return new \Database(
7 $this->db_host,
8 $this->db_user,
9 $this->db_pass
10 );
11 }
12 }
13 ?>

(注意我们如何在方法中使用$this而不是$di。)

然后,在我们的services.php文件中,可调用项成为此方法的引用,而不是内联闭包:

includes/services.php
1 2 $di->set('database', array($di, 'database'));
3 ?>

这很混乱,但可行。它也可能变得相当冗长。我们之前拆分Controller\ArticlesPage的示例最终看起来更像这样:

includes/services.php
1 2 // ...
3 $di->set('request', array($di, 'request'));
4 $di->set('response', array($di, 'response'));
5 $di->set('database', array($di, 'database'));
6 $di->set('Domain\Articles\ArticlesGateway', array($di, 'ArticlesGateway'));
7 $di->set('Domain\Users\UsersGateway', array($di, 'UsersGateway'));
8 $di->set(
9 'Domain\Articles\ArticleTransactions',
10 array($di, 'ArticleTransactions')
11 );
12 $di->set('Controller\ArticlesPage', array($di, 'ArticlesPage'));
13 ?>

classes/Di.php
1 2 class Di extends \Mlaphp\Di
3 {
4 public function request()
5 {
6 return new \Mlaphp\Request($GLOBALS);
7 }
8
9 public function response()
10 {
11 return new \Mlaphp\Response('/path/to/app/views');
12 }
13
14 public function database()
15 {
16 return new \Database(
17 $this->db_host,
18 $this->db_user,
19 $this->db_pass
20 );
21 }
22
23 public function ArticlesGateway()
24 {
25 return new \Domain\Articles\ArticlesGateway($this->get('database'));
26 }
27
28 public function UsersGateway()
29 {
30 return new \Domain\Users\UsersGateway($this->get('database'));
31 }
32
33 public function ArticleTransactions()
34 {
35 return new \Domain\Articles\ArticleTransactions(
36 $this->newInstance('ArticlesGateway'),
37 $this->newInstance('UsersGateway'),
38 );
39 }
40
41 public function ArticlesPage()
42 {
43 return new \Controller\ArticlesPage(
44 $this->get('request'),
45 $this->get('response'),
46 $this->user,
47 $this->newInstance('ArticleTransactions')
48 );
49 }
50 }
51 ?>

不幸的是,我们可能不得不打破一些样式约定,以使服务名称看起来像它们的相关方法名称。我们还必须将用于新实例的服务方法名称缩短为它们的结束类名,而不是它们的完全限定名。否则,我们会发现自己的方法名太长且容易混淆。

这可能很快让人困惑,但确实有效。总之,如果我们可以升级到 PHP5.3 或更高版本,那就更好了。

回顾和下一步

我们终于完成了现代化进程。我们不再有任何页面脚本。我们所有的应用程序逻辑都已转换为类,剩下的唯一包含文件是引导和设置过程的一部分。我们所有的对象创建逻辑都存在于一个容器中,在这个容器中,我们可以直接修改它,而不必干扰对象的内部。

在这之后,下一步可能是什么?答案是持续改进,这将持续你的职业生涯。


推荐阅读
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • Learning to Paint with Model-based Deep Reinforcement Learning
    本文介绍了一种基于模型的深度强化学习方法,通过结合神经渲染器,教机器像人类画家一样进行绘画。该方法能够生成笔画的坐标点、半径、透明度、颜色值等,以生成类似于给定目标图像的绘画。文章还讨论了该方法面临的挑战,包括绘制纹理丰富的图像等。通过对比实验的结果,作者证明了基于模型的深度强化学习方法相对于基于模型的DDPG和模型无关的DDPG方法的优势。该研究对于深度强化学习在绘画领域的应用具有重要意义。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
author-avatar
mobiledu2502914667
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有