Php 如何在Laravel 4中注入共享同一接口的多个类

Php 如何在Laravel 4中注入共享同一接口的多个类,php,dependency-injection,laravel,laravel-4,simplepie,Php,Dependency Injection,Laravel,Laravel 4,Simplepie,假设我有一个接口CrawlerInterface,带有实现PageCrawler和FeedCrawler;如果我们碰巧在一个控制器中同时需要这两个类,那么如何通过构造函数注入来实现呢 以前,我们使用一个中央服务提供商来注册(即App::bind)这样的类,但在大多数情况下,我们只有一个接口的实现,所以我们还没有遇到上述问题 PS:我还想知道这个问题是否表明我们应该拆分控制器 更新: 感谢您的评论和回复,为了解释,该接口只有一个公共方法:crawl($uri),页面/提要爬虫程序都将其实现为给定

假设我有一个接口
CrawlerInterface
,带有实现
PageCrawler
FeedCrawler
;如果我们碰巧在一个控制器中同时需要这两个类,那么如何通过构造函数注入来实现呢

以前,我们使用一个中央
服务提供商
来注册(即
App::bind
)这样的类,但在大多数情况下,我们只有一个接口的实现,所以我们还没有遇到上述问题

PS:我还想知道这个问题是否表明我们应该拆分控制器


更新:

感谢您的评论和回复,为了解释,该接口只有一个公共方法:
crawl($uri)
,页面/提要爬虫程序都将其实现为
给定资源标识符,返回资源。


我的后续问题:


假设我们在一个
计算器
场景中,加法、减法和乘法共享同一个接口
操作
,它只有一个公共方法
运行
,在某个时候我们仍然会遇到这个问题,对吗?一般来说,使用
ServiceProvider
,我们如何处理这种情况?

我认为在这种情况下,该界面对您没有帮助

通过这样做:

App::bind('CrawlerInterface', '<implementation>');

然后Laravel会注射它:

class CrawlerController {

    public function __construct(CrawlerInterface $crawler)
    {
    }

}
要两者兼得,你有两种选择

-有两个不同的接口

-直接注入实现:

class CrawlerController {

    public function __construct(PageCrawler $pageCrawler, FeedCrawler $feedCrawler)
    {
    }

}

但我也认为,如果您需要这样的东西,您最好重新考虑您的逻辑。

如果每个爬虫都是出于不同的原因存在的,您可以为您的实例使用任意名称,例如:

App::bind('crawler.allArticles', 'PageCrawler');
App::bind('crawler.latestArticles', 'FeedCrawler');
对于控制器:

App::bind('CrawlerController', function($app) {
    return new CrawlerController(
        App::make('crawler.allArticles'),
        App::make('crawler.latestArticles')
    );
});
然后,控制器代码将以不同方式使用每个爬虫:

public function showLatestArticlesAction()
    $latestArticles = $this->latestArticlesCrawler->crawl();
    // ...
}

public function showAllArticlesAction()
    $allArticles = $this->allArticlesCrawler->crawl();
    // ...
}

如果您只是有一个爬虫列表,其中每个爬虫都用于相同的事情,您可能需要执行以下操作:

App::bind('crawlers', function($app) {
    return [
        App::make('PageCrawler'),
        App::make('FeedCrawler'),
    ];
});
在控制器中,您将通过如下配置获得“爬虫”列表:

App::bind('CrawlerController', function($app) {
    return new CrawlerController(App::make('crawlers'));
});
您的控制器代码可能如下所示:

public function showArticlesAction()
    $allArticles = array();
    foreach ($this->crawlers as $crawler) {
        $allArticles = array_merge($allArticles, $this->crawler->crawl());
    }
    // ...
}

好的,假设您有一个
CrawlerController

class CrawlerController extends BaseController 
{
    protected $crawler1;
    protected $crawler2;

    public function __construct(CrawlerInterface $c1, CrawlerInterface $c2)
    {
        $this->crawler1 = $c1;
        $this->crawler2 = $c2;
    }
}
class PageCrawler implements CrawlerInterface{}
class FeedCrawler implements CrawlerInterface{}
接口

interface CrawlerInterface{}
以及称为
PageCrawler
FeedCrawler

class CrawlerController extends BaseController 
{
    protected $crawler1;
    protected $crawler2;

    public function __construct(CrawlerInterface $c1, CrawlerInterface $c2)
    {
        $this->crawler1 = $c1;
        $this->crawler2 = $c2;
    }
}
class PageCrawler implements CrawlerInterface{}
class FeedCrawler implements CrawlerInterface{}
您可以通过编写服务定位器来注入依赖项,如

App::bind('CrawlerController', function($app) {
    $controller = new CrawlerController(
        new PageCrawler,
        new FeedCrawler
    );
    return $controller;
});
但正如其他人建议的那样,你应该重新思考你的逻辑,只有在这种情况下才使用它
架构是不可避免的

正如一些人指出的那样,这实际上可能是一种代码味道(我自己肯定也遇到过这种味道!)。您能说明为什么选择使用接口来实现吗?两种实现之间有什么不同?正如Antonio所指出的,您可能真的希望使用具体的类作为依赖项,或者重新考虑一下代码体系结构。也许我们可以帮助解决这个问题的底层架构。@fideloper thx,我想我有一个解决问题的方法;但我仍然有点困惑,我们可以采取这种方法到什么程度,请看我的后续问题。我真的认为在一个接口上有几个实现并不是一件坏事。但是,如果是这种情况,并且您希望同时使用多个实现,那么应该有一个基于它们的用法的原因。例如,我的应用程序中可能有两个缓存:一个用于查询,另一个用于视图。然后我对查询使用“APCCache”,对视图使用“FileCache”,但正如您所见,我不关心实现,我只关心我有一个“cache.querys”和“cache.views”。使用方法很重要。@MatthieuNapoli我想知道如何与服务提供商进行管理……我不知道Laravel,但通常DI容器提供“命名服务”的概念,即不使用类/接口名称而是使用任意名称标识的服务。我会从容器中请求“cache.querys”条目,而不是“Doctrine\cache\CacheInterface”条目。嗯,我从未见过像这样的美国案例,我们真的应该从
App::bind
closure返回控制器吗?它有什么含义?因为我从来没有使用过这种方法,我不能肯定是否有任何含义,但乍一看,您是手动提供依赖项,而不是laravel通过反射来提供依赖项,所以应该没有任何问题。我使用的是
CrawlerInterface.page
-这是否像是有了PageCrawler的别名,它比直接注入类有优势吗?@bitinn在您的示例中,是的,它是相同的,您不应该这样做。名称应与其他用法有关。例如,在我的应用程序中,我有一个“logger.main”(日志错误)和一个“logger.querys”(日志数据库查询)。然后,我根据要记录的内容选择要记录的记录器,而不是根据要记录到文件、系统日志还是系统日志。因此,当您有用于不同内容的相同对象时,您应该“命名”您的服务。然而,在你的情况下,如果你只是想让“所有爬虫”来爬网所有可能的来源,那么第二个解决方案(在我的回答中)更好。似乎没有人想出更好的选择,我的第一个赏金是你的,谢谢你的帮助!