Tell, Don't Ask

Redjik

Джедай-мастер
Ищу хорошие статьи по сабжу.
Гугл выдает общую информацию.

Если можете объясните как работает принцип на примере сущностей Model, DbQuery, DataMaper.
Где model - orm, DbQuery - объект, отвечающий за запрос к базе, DataMaper думаю понятно.

ЗЫ.
и я пытаюсь понять Голливудский принцип (don't call us - we ll call u) это то же самое или нет
 

WMix

герр M:)ller
Партнер клуба
don't call us - we ll call u это больше похоже на Observer или event базированные системы / callback функции
по сабжу я понимаю следующее. очень часто, перед удалением, изменением некоего ресурса, идет ненужный запрос о его существовании,
вместо того чтоб при удалении несуществующего ресурса кинуть исключение. которое должно быть обработано как "ресурса не существует"
те. говори прямо что хочешь, не спрашивай имею/могу ли я это!
тыж не спрашиваешь перед каждым запросом есть ли соеденение с базой данных
 

Вурдалак

Продвинутый новичок
Tell, don't ask — это когда какое-то поведение основано на свойствах чужого объекта. Это своего рода нарушение инкапсуляции. То есть $object->getFoo() — плохо, $this->getFoo() — OK. Если что-то сильно зависит от состояния объекта, то лучше это поведение пихнуть в сам объект. С моделями вспоминается пример с renderTo. То есть вместо
PHP:
<?php

class Controller
{
	public function viewAction(User $user)
	{
		// ...

		return $this->render('view.twig', array(
			'name'     => $user->getName(),
			'birthday' => $user->getBirthday(),
			// ... 100500 свойств ...
		));
	}
}
можно писать
PHP:
<?php

class Controller
{
    public function viewAction(User $user)
    {
        // ...

        $view = $this->render('view.twig');
        $user->renderTo($view);
        return $view;
    }
}
ибо это выглядит как:
— *Controller* Модель, как там зовут чувака, день рождения, я тут запишу и отдам во View?
— *Model* Бла-бла, бла-бла
— *Controller* Я записал! View, вот, бла-бла, бла-бла
— *View* OK

vs

— *Controller* Модель, скажи что там нужно этому View
— *Model* OK

P.S. Хотя, честно говоря, сам так не пишу, передаю тупо сам объект. В случае AR это принципиально, я считаю, а если объект сам ничего не умеет (тупо Entity), то в общем-то это OK.
 

Redjik

Джедай-мастер
WMix
немного не то
“Procedural code gets information and then
makes decisions. OO code tells objects to do
things,” Alec Sharp in Smalltalk by Example.
Я более менее понимаю принцип и примеры в интернетах мне тоже понятны, но вот в контексте работы с бд, мне не понятно, ибо

ModelCreator -> (ask) данные у DbQuery
ModelCreator -> (tell) DataMapper создать Model по данным DbQuery
 

Вурдалак

Продвинутый новичок
Ещё кстати, пример из прошлых споров про то, как удалить файл :D

Вот в чём разница между
PHP:
if (is_file($fileName))
    unlink($fileName);
и
PHP:
try {
    unlink($fileName);
} catch (MissingFileException $e) { /* ... */ }
?

Первый нарушает subj, и создаёт ситуацию, когда файл может быть удалён между is_file() и unlink(). Во втором же случае мы нафиг не интересуемся существует ли он сейчас, потому что не наше это дело. Единственный аргумент — ну, мол, MissingFileException в PHP нет, это надо самому реализовывать где-то на уровне фреймворка. В .NET/Java таких проблем, например, нет.
 
Последнее редактирование:

Redjik

Джедай-мастер
Тут ведь нет ask?
PHP:
$dbQuery = DbQuery::factory('Driver',$sql);
$model = Model::factory(IDbQuery $dbQuery );

//внутри Model::factory()

return static::construct(new DataMapper(IDbQuery $dbQuery))
 
Последнее редактирование:

MiksIr

miksir@home:~$
Насчет Tell, Don't Ask насколько у меня в памяти осталось... основная идея - если что-то нужно сделать с объектом исходя из его состояния - то это должно быть внутри обьекта, но никак не снаружи. Т.е. нельзя откда-то извне спросить какой-то результат, и на его основе вызвать какой-то другой метод этого объекта.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Насчет Tell, Don't Ask насколько у меня в памяти осталось... основная идея - если что-то нужно сделать с объектом исходя из его состояния - то это должно быть внутри обьекта, но никак не снаружи. Т.е. нельзя откда-то извне спросить какой-то результат, и на его основе вызвать какой-то другой метод этого объекта.
Я бы сформулировал так: Tell, don't ask говорит о том, что вы не должны принимать решения о поведении объекта на основании запроса о его состоянии, а должны поручать ему действие, и ориентироваться на результат.

Грубый, но наглядный пример:
PHP:
if ($model->loaded())
{
 $model->update();
}
else
{
 $model->insert();
}
по TDA должно превращаться в
PHP:
$model->save();
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
нельзя откда-то извне спросить какой-то результат, и на его основе вызвать какой-то другой метод этого объекта.
главный вопрос, как я понимаю, можно ли передать результат в метод другого объекта?

PHP:
$result = $DbConnector->query($queryObject);
new ActiveRecord->fillWithQueryResult($result);
не лучше ли как-то вроде
$DbConnector->fillByQuery($queryObject, new ActiveRecord)

но тут явно ActiveRecord передается по цепочке вниз чтобы там в нем вызвали команду fill(), это лишь усложняет схему
 

флоппик

promotor fidei
Команда форума
Партнер клуба
главный вопрос, как я понимаю, можно ли передать результат в метод другого объекта?
Можно конечно, ты же не ask «в состоянии ли ты принять данные?», ты tell — «заполнись этими данными».

Хотя кмк, тут я бы тут предпочел
PHP:
$result->asActiveRecord();
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
$result->asActiveRecord(); нельзя потому что $result не знает какого класса наследника ActiveRecord должен быть объект
 

флоппик

promotor fidei
Команда форума
Партнер клуба
$result->asActiveRecord(); нельзя потому что $result не знает какого класса наследника ActiveRecord должен быть объект
Если DI через сервис локатор или DI-контейнер — то знает.
Ну очень хочется без управления зависимостями, то можно:
PHP:
$result->asModel(TraversableRecord new ActiveRecord);
Я не говорю что этот подход более правильный, просто он ближе к TDA по моему мнению.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
главное, чего не хватает - это поддержки в PHP core SPLBool-объектов, на которых можно создать null-object, чтобы можно не проверять состояние результата запроса после исполнения^
PHP:
$User = User::find($login);
View::render('template',['user'=>$User]);
а сейчас приходится
PHP:
$User = User::find($login);
if ($User->user_id){
  View::render('template',['user'=>$User]);
}else
 

флоппик

promotor fidei
Команда форума
Партнер клуба
главное, чего не хватает - это поддержки в PHP core SPLBool-объектов, на которых можно создать null-object, чтобы можно не проверять состояние результата запроса после исполнения^
PHP:
$User = User::find($login);
View::render('template',['user'=>$User]);
а сейчас приходится
PHP:
$User = User::find($login);
if ($User->user_id){
  View::render('template',['user'=>$User]);
}else
У нас это шаблонизатором решается:

{% for user in users %}
...
{% else %}
No records!
{% endfor %}

и {{ user|default('anonymouse') }}
 
Сверху