Как я могу использовать компоненты Yii, не отвлекаясь от сервисного уровня?
Мне нравится и использую фреймворк Yii, особенно его "компоненты", которые лениво создаются, и вы можете поменять их местами в файле конфигурации. Вроде как инъекция зависимости-lite.
Я стараюсь сохранить бизнес-логику моего кода полностью независимой от Framework, на случай, если мне захочется изменить этот код или даже изменить каркасы.
Допустим, у меня есть класс в моем сервисном слое под названием AccountService, который реализует IAccountService и имеет конструктор с одним аргументом.
interface IAccountService
{
function getUserById($id);
}
class AccountService implements IAccountService
{
private $_userRepository;
public function __construct(IUserRepository $userRepository) {
$this->_userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
Отлично. Пока что это полностью без фреймворка. Теперь я хотел бы представить это как компонент Yii, чтобы его можно было легко создавать и легко использовать контроллерами Yii и другими компонентами Yii.
Но компоненты Yii (которые реализуют IApplicationComponent) должны иметь ровно нулевые аргументы конструктора, в то время как мой класс требует один!
Есть идеи?
Вот что у меня есть. Я не очень доволен ни одним из них; они оба выглядят чрезмерно спроектированными, и я чувствую отчетливый запах от них.
Вариант 1 - составить: я создаю класс с именем "AccountServiceComponent", который реализует IApplicationComponent Yii. Он не может расширить мой класс AccountService из-за конструктора, но он может создать его экземпляр как закрытый член и обернуть все его методы следующим образом:
class AccountServiceComponent implements IApplicationComponent, IAccountservice
{
private $_accountService;
public __construct() {
$this->_accountService = new AccountService(new UserRepository());
}
public getUserById($id) {
return $this->_accountService->getUserById($id);
}
}
Минусы: мне придется обернуть каждый метод таким образом, что утомительно и может привести к "пахлавному коду". Особенно с учетом того, что будет несколько классов обслуживания, каждый с несколькими методами.
Вариант 2 - миксин: (или поведение, или черта, или как это называется в наши дни.)
Yii (написанный до PHP 5.4) предлагает "поведение" в форме класса, который реализует IBehavior. Я мог бы создать класс поведения, который расширяет мой сервис, и прикрепить его к компоненту:
class AccountServicesBehavior extends AccountService implements IBehavior
{
// Implement the few required methods here
}
class AccountServiceComponent implements IApplicationComponent
{
public function __construct() {
$accountService = new AccountService(new UserRepository());
$this->attachBehavior($accountService);
}
Минусы: Мой компонент больше официально не реализует IAccountService. Также, кажется, становится чрезмерным с наслоением.
Вариант 3 - необязательные параметры конструктора:
Я мог бы просто сделать параметр конструктора для моего класса обслуживания необязательным, а затем расширить его до компонента:
class AccountService implements IAccountService
{
public $userRepository;
public function __construct(IUserRepository $userRepository = null) {
$this->userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
class AccountServiceComponent extends AccountService implements IApplicationComponent
{
}
Минусы: необязательный параметр конструктора означает, что этот класс coudld теперь может быть создан без предоставления ему всего, что ему нужно.
... так, какие-либо другие варианты, которые я пропускаю? Или мне просто нужно выбрать тот, который меня беспокоит меньше всего?
1 ответ
Вариант 3, но с объектом в качестве необязательного аргумента лучше всего звучит imo:
public function __construct(IUserRepository $userRepository = new UserRepository()) {
$this->userRepository = $userRepository;
}