Как добавить несколько классов, которые используют один и тот же интерфейс в Laravel 4
Скажем, у меня есть интерфейс CrawlerInterface
с реализацией PageCrawler
и FeedCrawler
; если нам понадобятся оба класса в контроллере, как это может быть достигнуто при вводе конструктора?
Раньше мы использовали центральный ServiceProvider
для регистрации (то есть App::bind
) таких классов, но в большинстве случаев у нас есть только 1 реализация интерфейса, поэтому эта проблема еще не произошла с нами.
PS: Я также задаюсь вопросом, предполагает ли эта проблема разделить контроллер.
Обновление:
Спасибо за комментарии и ответ, чтобы объяснить, что интерфейс имеет только один общедоступный метод: crawl($uri)
, и оба искателя страницы/фида реализуют его как given a resource identifier, return resource.
Мой следующий вопрос:
Скажем, что мы находимся в сценарии calculator
, где добавление, вычитание и умножение имеют один и тот же интерфейс Operation
, который имеет только 1 открытый метод run
, в какой-то момент мы все равно столкнемся с этой проблемой правильно? Как мы вообще обрабатываем такую ситуацию с помощью ServiceProvider
?
Ответы
Ответ 1
Если каждый искатель существует по другой причине, вы можете использовать произвольные имена для своих экземпляров, например:
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());
}
// ...
}
Ответ 2
Ok позволяет предположить, что у вас есть CrawlerController
class CrawlerController extends BaseController
{
protected $crawler1;
protected $crawler2;
public function __construct(CrawlerInterface $c1, CrawlerInterface $c2)
{
$this->crawler1 = $c1;
$this->crawler2 = $c2;
}
}
интерфейс
interface CrawlerInterface{}
и конкретные реализации этой функции, называемой PageCrawler
и FeedCrawler
class PageCrawler implements CrawlerInterface{}
class FeedCrawler implements CrawlerInterface{}
Вы должны вводить зависимости, записывая локатор службы, например
App::bind('CrawlerController', function($app) {
$controller = new CrawlerController(
new PageCrawler,
new FeedCrawler
);
return $controller;
});
Но, как было предложено другими, вы должны пересмотреть свою логику, использовать ее только в том случае, если этот вид
архитектуры неизбежно
Ответ 3
Я думаю, что интерфейс в этом случае не поможет.
Выполняя:
App::bind('CrawlerInterface', '<implementation>');
Вам нужно выбрать один:
App::bind('CrawlerInterface', 'PageCrawler');
или
App::bind('CrawlerInterface', 'FeedCrawler');
И тогда Laravel будет его вводить:
class CrawlerController {
public function __construct(CrawlerInterface $crawler)
{
}
}
У вас есть 2 варианта
- имеет два разных интерфейса
- Внедрить реализации напрямую:
class CrawlerController {
public function __construct(PageCrawler $pageCrawler, FeedCrawler $feedCrawler)
{
}
}
Но я также думаю, что если вам нужно что-то вроде этого, вы лучше переосмыслите свою логику.