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

Моей ошибкой было начинать смотреть касты про аукцион, не ознакомившись с подходами, освещенными в серии "Взаимодействие объектов". Благодарю.
Кстати, будет здорово, если поделитесь мыслями о желательном порядке изучения Ваших материалов. А уже каждый подумает, какой у него уровень и с какого каста/курса начать.
Советую интенсив по ООП посмотреть - значительно поможет освоится.
А где этот интенсив?
На моём сайте https://elisdn.ru/products
Вопрос по разделению ответственности Сервисов
Есть сервис регистрации 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.
Да, лучше сделать отдельный сервис, чтобы не смешивать разные способы регистрации в одном. В соседних скринкастах по аукциону мы всегда для каждой операции делаем отдельный класс-команду. Тогда вопросов с гранью не возникает.
Можно ли в сущности использовать сервисы?
В методе 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-ов и изменение одного метеода никак не ломает другие методы и тесты.
В видео говорится что скрывать свойства объекта нужно для того чтобы какой-то другой разработчик их не мог "испортить". Это неверно, на самом деле сокрытие реализации нужно для того чтобы разработчики не писали код основываясь на деталях реализации объекта, так как код основанный на деталях реализации приводит к лишней связности (между объектами).
Это неверно
Не "неверно", а "не только". Защита от порчи и сокрытие деталей друг другу не противоречат, а дополняют.
Дмитрий, было бы не лишним для ваших слушателей, если бы вы рассказали о фундаментальных характеристиках программ. В частности, о таких понятиях как coupling и cohesion, на которых основываются все базовые принципы (например, SOLID) и шаблоны проектирования.
+
+
+
+