糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 依赖注入与pimple

依赖注入与pimple

时间:2019-09-09 19:10:07

相关推荐

依赖注入与pimple

在应用程序开发中,我们尝试创建独立的模块,以便我们可以在将来的项目中重用代码。但是,很难创建完全独立的模块来提供有用的功能。它们的依赖关系可能导致维护方面的噩梦,除非对其进行适当的管理。这就是依赖关系注入派上用场的地方,因为它使我们能够注入代码正常运行所需的依赖关系,而无需将其硬编码到模块中。

Pimple是一个简单的依赖项注入容器,它利用PHP的闭包以可管理的方式定义依赖项。在本文中,我们将研究硬编码依赖关系的问题,依赖关系注入如何解决它们,以及如何使用Pimple保持利用依赖关系注入的代码的可维护性。

具体依赖问题

在编写应用程序时,我们使用许多PHP类。一个类可能需要调用一个或多个其他类的方法来提供预期的功能,因此我们说第一类依赖于其他类。例如:

<?phpclass A{public function a1() {$b = new B();$b->b1();}}

classA取决于classB。如果classB不可用,则以上代码将不起作用。

而且,每当我们在一个类内对对象的创建进行硬编码时,我们都会对该类进行具体的依赖。具体的依赖关系是编写可测试代码的障碍。更好的方法是为classA提供class B的对象。这些对象可以通过提供A的构造函数或setter方法。

在继续之前,让我们看一个更现实的场景。

如今,在社交网站上共享内容非常普遍,并且大多数网站都在其网站上直接显示其社交资料。假设我们有一个名为SocialFeeds的类,该类从诸如Weibo,Wechat,等社交网站生成提要。将创建单独的类来与这些服务中的每一个一起使用。在这里,我们将看一下与Weibo交互的类WeiboService

SocialFeeds类要求使用Weibo的提供WeiboService。WeiboService与数据库进行交互以检索特定的用户token以访问API。token传递给OAuth类,该类使用提供的token检索提要,并将其返回给SocialFeeds类。

<?phpclass SocialFeeds{public function getSocialFeeds() {$twService = new WeiboService();echo $twService->getTweets();}}

<?phpclass WeiboService{public function getTweets() {$db = new DB();$query = "Query to get user token from database";$token = $db->getQueryResults($query);$oauth = new OAuth();return $oauth->requestWeiboFeed($token);}}

<?phpclass OAuth{public function requestWeiboFeed($token) {// Retrieve and return Weibo feed using the token }}

<?phpclass DB{public function getQueryResults($query) {// Get results from database and return token}}

显然,这SocialFeeds取决于WeiboService。但是,WeiboService取决于DBOAuthSocialFeeds依赖于两个DBOAuth间接的影响。

那么会有什么问题呢?SocialFeeds取决于三个类的具体实现,因此如果SocialFeeds没有其他类的实际实现,则不可能作为一个单独的单元进行测试。或者,假设我们要使用其他数据库或其他OAuth提供程序。在这种情况下,我们在每次代码中每次出现时都必须用新类替换现有类。

修复依赖性

解决这些依赖性问题的方法很简单,就是在需要时不使用具体实现就动态地提供对象。注入依赖关系有两种技术:基于构造函数的依赖关系注入和基于setter的注入。

基于Constructor的注入

通过基于Constructor的依赖注入,可以在外部创建依赖对象,并将其作为参数传递给类的构造函数。我们可以将这些对象分配给类变量,并在类中的任何地方使用。SocialFeeds该类的基于Constructor的注入如下所示:

<?phpclass SocialFeeds{public $wbService;public function __construct($wbService) {$this->wbService = $wbService;}public function getSocialFeeds() {echo $this->wbService->getTweets();}}

WeiboService的实例作为对象传递给构造函数。SocialFeeds仍然依赖WeiboService,但是现在我们可以自由地提供不同版本的Weibo,甚至可以提供模拟对象用于测试目的。在DBOAuth类对于类似的规定WeiboService

<?php$db = new DB();$oauth = new OAuth();$wbService = new WeiboService($db, $oauth);$socialFeeds = new SocialFeeds($wbService);$socialFeeds->getSocialFeeds();

基于Setter的注入

对于基于setter的注入,对象是通过setter方法而不是Constructor提供的。这是SocialFeeds该类的基于setter的依赖项注入的实现:

<?phpclass SocialFeeds{public $wbService;public function getSocialFeeds() {echo $this->wbService->getTweets();}public function setWeiboService($wbService) {$this->wbService = $wbService;}}

初始化代码包括DBOAuth

<?php$db = new DB();$oauth = new OAuth();$wbService = new WeiboService();$wbService->setDB($db);$wbService->setOAuth($oauth);$socialFeeds = new SocialFeed();$socialFeeds->setWeiboService($wbService);$socialFeeds->getSocialFeeds();

Constructorvs Setter

由你决定是在Constructor还是基于setter的注入之间进行选择。当实例化类需要所有依赖项时,基于Constructor的注入是合适的。当不需要在每种情况下都依赖时,基于Setter的注入是合适的。

优点

Constructor–只需查看类的构造函数即可识别类的所有依赖关系Setter –添加新的依赖项就像添加新的setter方法一样简单,这不会破坏现有代码

缺点

Constructor–添加新的依赖关系会增加构造函数的参数;现有代码需要在我们的整个应用程序中进行更新,以提供新的依赖关系Setter-我们必须手动搜索必要的依赖项,因为它们在任何地方都没有指定

有了依赖注入和各种注入技术的知识,现在该看看Pimple并了解它的适用范围。

Pimple在依赖注入的使用

您可能想知道为什么当我们可以使用前面提到的技术注入依赖项时,为什么需要Pimple。要回答这个问题,我们需要参考DRY原理。

不要自己重复 Don’t Repeat Yourself (DRY)是软件开发的原则,旨在减少各种信息的重复,在多层体系结构中尤其有用。DRY原则表示为“系统中的每条知识都必须具有单一,明确,权威的表示形式

考虑基于构造函数的注入示例。每次我们想要一个SocialFeed类的对象时,我们都必须重复实例化和传递其依赖关系的整个设置过程。根据DRY,应避免使用此类代码以防止维护麻烦。Pimple充当定义此类依赖项以避免重复的容器。

让我们看一个简单的例子,看看Pimple是如何工作的。

<?php$container = new Pimple();$container['class_name'] = 'Test';$container['object_name'] = function ($c) {return new $c['class_name']();};$testClass = $container['object_name'];

Pimple创建的实例以用作存储依赖项的容器。它实现了SPLArrayAccess接口,因此使用它与使用数组非常相似。首先,我们定义了一个键,其中包含我们想要的任意类的名称。然后,我们定义了一个闭包以返回指定类的实例,该实例充当服务。注意,$c将传递一个容器的实例,因此我们可以根据需要引用其他定义的键。每个定义的参数或对象都可以通过$c变量在闭包中使用。现在,只要我们想要该类的实例,就可以引用该键以检索该对象。

让我们将SocialFeeds示例转换为Pimple。Pimple官方网站上的示例显示了基于构造函数的注入,因此这里我们将说明基于setter的注入。请记住,使用Pimple不需要修改我们先前定义的任何setter方法或代码,我们只需封装逻辑即可。

<?php$container = new Pimple();$container['oauth'] = function($c) {return new OAuth();};$container['db'] = function($c) {return new DB();};$container['tweet_service'] = function($c) {$wbService = new WeiboService();$wbService->setDB($c['db']);$wbService->setOauth($c['oauth']);return $wbService;};$container['social_feeds'] = function($c) {$socialFeeds = new SocialFeeds();$socialFeeds->setweiboService($c['tweet_service']);return $socialFeeds;};$socialFeeds = $container['social_feeds'];$socialFeeds->getSocialFeeds();

这两个DBOAuth类是独立的模块,所以我们直接封内返回他们的新实例。然后,我们WeiboService使用基于setter的注入将依赖项添加到类中。我们已经将DBOAuth类添加到了容器中,因此我们可以使用$c['db']和直接在函数内部访问它们$c['oauth']

现在,依赖项作为服务封装在容器内。每当我们想要使用不同的DB类或不同的OAuth服务时,我们只需替换容器语句中的类即可,一切将正常运行。使用Pimple,您只需要在一个地方添加新的依赖项。

Pimple进阶用法

在上述情况下,只要有一个请求,Pimple都会从闭包中返回每个类的新实例。在某些情况下,我们需要使用同一对象而无需每次都初始化新实例,例如,连接数据库是一个很好的例子。

Pimple提供了使用共享对象返回相同实例的功能,这样做需要我们通过share()如下所示的方法指定闭包:

<?php$container['db'] = $container->share(function ($c) {return new DB();});

同样,到目前为止,我们已经在Pimple容器内的单个位置定义了所有依赖项。但是考虑一下这样一种情况,我们需要具有依赖项但配置方式与原始配置略有不同的服务。例如,假设我们需要访问WeiboService该类的某些功能的ORM。我们无法更改现有的闭包,因为它将强制所有现有的功能使用ORM。

Pimple提供了一种extend()在不影响原始实现的情况下动态修改现有闭包的方法。代码:

<?php$container['tweet_service'] = $container->extend('tweet_service', function($twSservice, $c) {$wbService->setDB(new ORM());return $wbService;});

现在,我们可以tweet_service在特殊情况下使用不同的扩展版本。第一个参数是服务的名称,第二个参数是用于访问对象实例和容器的函数。

确实,这extend()是一种动态添加依赖项以适应不同情况的有效方法,但是请确保将服务的扩展版本限制在最低限度,因为这会增加重复代码的数量。

如果觉得《依赖注入与pimple》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。