Tell, don't ask

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Хм... Похоже, главная мотивация принципа tell don't ask заключается в уменьшении дублирования кода (дублирование одних и тех же проверок в разных местах программы)
Если это так, то в примере с кешированием, если используется весь этот код в нескольких местах, то конечно надо рефакторить. Если же используется только вызов метода, который весь этот код содержит, тогда всё ок.
Правильно?

Или есть еще какие-то выгоды от этого принципа?
 

HraKK

Мудак
Команда форума
Облегчение понимая и разветвленности программы.
 

fixxxer

К.О.
Партнер клуба
Хм... Похоже, главная мотивация принципа tell don't ask заключается в уменьшении дублирования кода (дублирование одних и тех же проверок в разных местах программы)
Если это так, то в примере с кешированием, если используется весь этот код в нескольких местах, то конечно надо рефакторить. Если же используется только вызов метода, который весь этот код содержит, тогда всё ок.
Правильно?

Или есть еще какие-то выгоды от этого принципа?
Я бы так сказал - не надо извне узнавать какие-то параметры, которые в рамках данного класса нужны только для того, чтобы передать кому-то другому.

Пример (допустим это код какого то контроллера):

Ask:


$Message = $MessageDispatcher->createMessage();
$email = $User->getEmailAddress(); // и зачем мне здесь знать, что у юзера есть свойство email?
$Message->sendTo($email);

Tell:

$MessageDispatcher->createMessageFor($User)->send();
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Tell:

$MessageDispatcher->createMessageFor($User)->send();
Тогда получается:

Supertell:
$MessageDispatcher->sendMessageFor($User);

Ведь клиенту не за чем знать, что создается объект Message?
Мы возвращаемся к первому посту топика
 

fixxxer

К.О.
Партнер клуба
В принципе да, но это уже не так важно - на мой взгляд, главное тут - не завязываться на внутренние характеристики объектов, которые тебе не нужны
 

Активист

Активист
Команда форума
> $MessageDispatcher->createMessageFor($User)->send();

fixxxer
Гавно какое-то, это же связи получаются, т.е. код превратиться в унылое гавно, все будет завязано, кроме того, $MessageDispatche начнет знать чего-то о том, о чем знать ему не положено, нарушаем много чего.


А может быть так:
$email = $User->sendNotify();
а в sendNotify уже сам решает, чего ему делать, ничего не спрашивая.
типа
$MessageDispatcher->sendMessage($this->email, $this->name, $this->body);
 

AmdY

Пью пиво
Команда форума
Активист
вот фиксер как раз и говорит, что не надо завзываться на внутренних характеристиках, главное. чтобы они удоблетворяли интерфейсу.
PHP:
//не
$MessageDispatcher->sendMessage($this->email, $this->name, $this->body);
//а 
$MessageDispatcher->sendMessage($this)
 

fixxxer

К.О.
Партнер клуба
А может быть так:
$email = $User->sendNotify();
а в sendNotify уже сам решает, чего ему делать, ничего не спрашивая.
типа
$MessageDispatcher->sendMessage($this->email, $this->name, $this->body);
О, я ждал, пока кто-то напишет. :D

Да, ты прав. Но, если пойти дальше, получается, что юзер знает о каких-то там сообщениях. А задача отправить сообщение с каким-то текстом (то есть шаблон по сути), который пользователь должен запомнить. Получается как-то так:

$User->renderInto($Template);
$MessageDispatcher->createMessageFromTemplate($Template)->send();

Но e-mail как простой параметр шаблона выглядит тоже странно. Думаем дальше :) С одной стороны, сообщение может знать о том, что есть нечто implements IRecipient { function getEmailAddress() }. С другой стороны (раз уж нам так не нравятся геттеры, хотя в контексте _нужного_ знания я ничего такого не вижу), пользователь может (раз у него уж есть e-mail) заполнить сообщение своим адресом...
 

Активист

Активист
Команда форума
AmdY
$MessageDispatcher->sendMessage($this)

Это ж еще большая связь получается, а если мне вдруг, надо будет сменить шаблон....

Ничего плохого в этом
PHP:
$MessageDispatcher->sendMessage($this->email, $this->name, $this->body);

Вообще я понимаю этот принцип так:
Tell:
PHP:
$message = $MessageDispatcher->create();
$message->addRecipient($email);
$message->setBody("Ой! Кажется админ ПФР удалил  вас из БД :( Не огорчайтесь, сегодня в магазине рядом идет распродажа веревок и мыла :) ");
$message->send();

Ask:
PHP:
$message = $MessageDispatcher->create();
$message->addRecipient($email);
if ($message->method = "sendmail") {
$message->setAltBody();
} else {
$message->setHTMLBody();
}
$message->send();
 

Духовность™

Guest
А может быть так:
PHP:
$email = $User->sendNotify();
Пользователь априори не умеет отправлять письма, это задача электронной почты или почтового отделения. Т.е. отдельного объекта.

Tell:
PHP:
 $MessageDispatcher->createMessageFor($User)->send();
как то нифига не гибко и интуитивно не понятно.

У меня так сделано:

PHP:
// вася написал письмо 
$mail = new Base_Mail();
$mail->setFrom( Base_Registry::getInstance()->config['robot_email_adress'] );
$mail->setReplyTo( Base_Registry::getInstance()->config['robot_email_adress'] );
$mail->setHeader('Восстановление забытого пароля на сайте');
$mail->setTemplate($this->getTemplateFilePath('FrontendGetpasswordSendTest'));

try {
     // захотел отправить его через Главпочтамт 
     $service = new Module_User_Service_Getpassword();
     // зашел в почту
     $service->setUser($user)
     // отдал письмо 
     ->setMail($mail)
     // пота отправила письмо через свои механизмы, не известные васе
     ->sendEmailWithHash();
} catch ...
 

Активист

Активист
Команда форума
> априори не умеет отправлять письма
Окей!

PHP:
$User->tellSelfToGoToPostAndSendLetterNOW();
...
method tellSelfToGoToPostAndSendLetterNOW() {
$User->giveAPan();
$User->giveASheets();
$User->writeAddress();
$User->writeMessage();
$User->addRegads();
$User->walkToPost();
$User->putLetterToMailbox();
$User->tellPostOfficeAboutYouLetter();
$User->profit();
}
 

Духовность™

Guest
> априори не умеет отправлять письма
Окей!

PHP:
$User->tellSelfToGoToPostAndSendLetterNOW();
...
method tellSelfToGoToPostAndSendLetterNOW() {
$User->giveAPan();
$User->giveASheets();
$User->writeAddress();
$User->writeMessage();
$User->addRegads();
$User->walkToPost();
$User->putLetterToMailbox();
$User->tellPostOfficeAboutYouLetter();
$User->profit();
}
и?
Пользователь априори не умеет отправлять письма, это задача электронной почты или почтового отделения. Т.е. отдельного объекта.
 

Активист

Активист
Команда форума
Это я к тому, что пользователь должен сделать определенные действия, что бы отправить письмо, т.е., подготовить все необходимое для его отправке и передать в почтовое отделение (tell), а не просить (ask) почтовое отделение перезвонить ему и уточнить, какой у него адрес, что будет в письме.

После того, как он его передаст в почтовое отделение (tell), он выполнит свою работу.

Это принцип Tell, don't ask, на пальцах.
 

Духовность™

Guest
Это я к тому, что пользователь должен сделать определенные действия, что бы отправить письмо, т.е., подготовить все необходимое для его отправке и передать в почтовое отделение (tell), а не просить (ask) почтовое отделение перезвонить ему и уточнить, какой у него адрес, что будет в письме.

После того, как он его передаст в почтовое отделение (tell), он выполнит свою работу.

Это принцип Tell, don't ask, на пальцах.
Совершенно правильно. Но как вяжется это утверждение с этим $User->tellSelfToGoToPostAndSendLetterNOW ? У тебя пользователь делает SendLetterNOW. Он не должен это уметь.

и вообще это меня пугает:
PHP:
$User->giveAPan();
$User->giveASheets();
$User->writeAddress();
$User->writeMessage();
$User->addRegads();
$User->walkToPost();
$User->putLetterToMailbox();
$User->tellPostOfficeAboutYouLetter();
$User->profit();
Это реальный код?
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Опять же возвращаемся к первому посту. Получается, что юзер должен уметь слишком много, туеву хучу вещей. Это будет такой класс, что не приведи господь
 

Активист

Активист
Команда форума
Ну он может нанять профессионального копирайтера, который будет за него придумывать текст, и это будет его задача, в любой случае это будет не проблема почтового отделения, и говорить он будет копирайтеру или подписать договор конторой, а тот в свою очередь относить письма в почту (или передавать конкретному копирайтеру в случае если контора).
 

whirlwind

TDD infected, paranoid
Вы сразу пытаетесь понять какие-то непостижимые тонкости. Для начала просто избавьтесь от большинства if/else на результаты вызовов. Я не зря в #17 отпостил Шарпа. Фича в том, что if/else перенести туда, где реальная зона ответственности, а не где попало. Например, пример про user вообще лишен смысла. Где вы там внутри отправки мыла if/else написали?

PS. ask - это и есть if. А tell отсутствие if-а.
 
Сверху