Как я могу использовать компоненты 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;
}
Другие вопросы по тегам