Конструкторы и зависимости

Использование конструкторов для инициализации объектов. Внедрение зависимостей. Разница конструкторов сущностей и сервисов.

  • 00:00:20 - Geometry
  • 00:01:00 - Point
  • 00:06:10 - Circle
  • 00:10:34 - User
  • 00:17:17 - Email
  • 00:18:33 - Phone
  • 00:21:00 - PHP
  • 00:22:48 - Именованные конструкторы
  • 00:26:30 - Сущности и сервисы
  • 00:27:17 - PasswordHasher
  • 00:28:52 - Handler
  • 00:30:54 - ConfirmTokenSender
  • 00:35:32 - Подведение итогов
Скрытый контент (код, слайды, ...) для подписчиков. Открыть →
Дмитрий Елисеев
elisdn.ru
Комментарии (18)
Сергей

Дмитрий, было бы не лишним для ваших слушателей, если бы вы рассказали о фундаментальных характеристиках программ. В частности, о таких понятиях как coupling и cohesion, на которых основываются все базовые принципы (например, SOLID) и шаблоны проектирования.

Ответить
Сулейман

+

Ответить
Артем

+

Ответить
Владимир Перепеченко

+

Ответить
S.Polessky

+

Ответить
Владимир Перепеченко

Моей ошибкой было начинать смотреть касты про аукцион, не ознакомившись с подходами, освещенными в серии "Взаимодействие объектов". Благодарю.

Ответить
Владимир Перепеченко

Кстати, будет здорово, если поделитесь мыслями о желательном порядке изучения Ваших материалов. А уже каждый подумает, какой у него уровень и с какого каста/курса начать.

Ответить
S.Polessky

Советую интенсив по ООП посмотреть - значительно поможет освоится.

Ответить
Сидредин

А где этот интенсив?

Ответить
S.Polessky

Вопрос по разделению ответственности Сервисов

Есть сервис регистрации SignupService, который выполняет регистрацию пользователя.

В наш магазин мы добавляем возможность гостевого заказа, но чтобы сразу пользователя регистрировало на основе его данных. Получается такой метод:

public function signupByGuestOrder(OrderGuestForm $guestForm, $ip, $partnerId)
{
    $username = $this->usernameUniqueService->process((explode("@", $guestForm->email))[0]);
    $user = User::requestSignupByOrder($username, $guestForm->email, $ip, $partnerId);
    $this->users->add($user);
}

Вопрос: Нам следует эту логику поместить в отдельный SignupByOrderService или можно оставить в SignupService? Где та самая грань, когда надо создавать новый сервис?

P.S. В данном случае оставил в SignupService.

Ответить
Дмитрий Елисеев

Да, лучше сделать отдельный сервис, чтобы не смешивать разные способы регистрации в одном. В соседних скринкастах по аукциону мы всегда для каждой операции делаем отдельный класс-команду. Тогда вопросов с гранью не возникает.

Ответить
S.Polessky

Можно ли в сущности использовать сервисы?

В методе signupByGuestOrder с помощью UsernameUniqueService мы проверяем уникальность имени/генерируем уникальное:

public function signupByGuestOrder(OrderGuestForm $guestForm, $ip, $partnerId)
{
    $username = $this->usernameUniqueService->process((explode("@", $guestForm->email))[0]);
    $user = User::requestSignupByOrder($username, $guestForm->email, $ip, $partnerId);
    $this->users->add($user);
}

Но нам было бы удобнее генерировать username в именованном конструкторе User::requestSignupByOrder, как мы это делаем с паролем:

public static function requestSignupByOrder($username, $email, $ip, $partnerId)
{
    $password = Yii::$app->security->generateRandomString(8);
    $user = self::requestSignup($username, $email, $password, $ip, $partnerId);
    $user->recordEvent(new SignupByOrderEvent($user, $password));

    return $user;
}

Можно ли в сущности использовать сервисы?

Ответить
Дмитрий Елисеев

Можно, если сервис передавать в сам метод:

public static function requestSignupByOrder($username, $email, Security $security)
{
    $password = $security->generateRandomString(8);
    $user = self::requestSignup($username, $email, $password);
    $user->recordEvent(new SignupByOrderEvent($user, $password));

    return $user;
}

Об этом опубликовал статью о внедрении зависимостей в своём блоге.

Но понятнее будет вместо большого компонента Security передавать свой простой генератор, который внутри уже будет вызывать generateRandomString.

public static function requestSignupByOrder($username, $email, PasswordGenerator $generator)
{
    $password = $generator->generate();
    $user = self::requestSignup($username, $email, $passwordd);
    $user->recordEvent(new SignupByOrderEvent($user, $password));

    return $user;
}

Это будет удобно и в юнит-тестах, когда там будем делать стаб или мок для этого генератора.

Ответить
Иван

Дмитрий, такой вопрос:

public function __construct($id, Phone $phone=null, Email $email=null) {}

А разве такого вида не будет работать ? И делать уже внутри конструктора проверку на NULL

И чем плох такой метод?

Ответить
Дмитрий Елисеев

Здесь отличие всего в двух параметрах, но их может быть больше. Например, для трёх способов регистрации пользователя может быть такой код:

class User
{
    pubic function __construct(
        $id,
        $date,
        $email=null,
        $phone=null,
        $password=null,
        $token=null,
        $network=null
    ) {}
}

Такой код плох неявностью. Непонятно, что и когда ему нужно передавать. Нужно будет отдельно прописывать к нему большую инструкцию.

Плох внутрренней запутанностью и сложностью. Внутри такого метода будет куча if-ов для проверки всех комбинаций.

Плох связанностью. Любое добавление или изменение параметров для изменения или добавления каждого способа сразу сломает кучу кода и тестов, которые дёргают этот метод.

А если у нас отдельные методы:

static function requestSignUpByEmail($id, $date, $email, $token) {}
static function signUpByPhone($id, $date,$phone, $code) {}
static function signUpByNetwork($id, $date, $email)

то всё понятно с первого взгляда, все методы без if-ов и изменение одного метеода никак не ломает другие методы и тесты.

Ответить
Дмитрий

В видео говорится что скрывать свойства объекта нужно для того чтобы какой-то другой разработчик их не мог "испортить". Это неверно, на самом деле сокрытие реализации нужно для того чтобы разработчики не писали код основываясь на деталях реализации объекта, так как код основанный на деталях реализации приводит к лишней связности (между объектами).

Ответить
Дмитрий Елисеев

Это неверно

Не "неверно", а "не только". Защита от порчи и сокрытие деталей друг другу не противоречат, а дополняют.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Yandex
MailRu
GitHub
Google