Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 06:50
Оценка:
Здравствуйте.
Читаю книгу "Программист-прагматик", там один из советов — придерживаться закона Деметера. Закон звучит так:

Любой метод объекта должен обращаться только к методам, принадлежащим:
— тому же классу
— любым объектам, переданным в метод в качестве аргументов
— любым создаваемым им объектам
— любым непосредственно содержащимся объектам компонентов


То есть

void Demeter::example(B& b){
  b.invert(); // Так можно
  
  b.getUser.name(); // Так не рекомендуется
}


Аргументация в том, что уменьшается связанность и упрощается изменение классов. Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?

1 вариант (нарушение закона):
void обслуживание(Машина машина){
  машина.левоеПереднееКолесо().накачать();
}


2 вариант (добавление лишнего(?) метода в класс "Машина"):
void обслуживание(Машина машина){
  машина.накачатьЛевоеПереднееКолесо();
}


3 вариант (добавление лишнего(?) метода в текущий класс):
void обслуживание(Машина машина){
  колесо = машина.левоеПереднееКолесо(); 
  накачать(колесо);
}

void накачать(Колесо колесо){
  колесо.накачать();


Как вы считаете, какой из вариантов более аккуратный? Какой облегчает поддержку и дальшейшие изменения программы? Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?
Re: Закон Деметера на практике
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 18.02.07 08:53
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

Имхо, второй вариант бессмысленный.
Третий вариант имеет смысл если метод "накачать(Колесо колесо)" будет вынесен из "Машины" в, например, класс "Насос".

При именно такой постановке задания, я бы остановился на первом варианте Но при первой же необходимости вырефакторил бы в третий вариант.

MP> Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?


Желательно придерживаться.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re: Закон Деметера на практике
От: DrDred Россия  
Дата: 18.02.07 09:26
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

MP>Аргументация в том, что уменьшается связанность и упрощается изменение классов. Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?


void ТО(Машина машина)
{
    накачать_колеса(машина.getWheels())
}
void накачать_колеса(Колесо[] колеса)
{
    foreach(колесо in колеса) {}
}
... << RSDN@Home 1.2.0 alpha rev. 672>>
--
WBR, Alexander
Re[2]: Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 10:24
Оценка:
Здравствуйте, DrDred, Вы писали:

DD>Здравствуйте, Mikhail Polykovsky, Вы писали:


MP>>Аргументация в том, что уменьшается связанность и упрощается изменение классов. Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?


DD>
DD>void ТО(Машина машина)
DD>{
DD>    накачать_колеса(машина.getWheels())
DD>}
DD>void накачать_колеса(Колесо[] колеса)
DD>{
DD>    foreach(колесо in колеса) {}
DD>}
DD>


Я правильно понял, что это за третий вариант?
Re[2]: Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 10:27
Оценка:
ANS>При именно такой постановке задания, я бы остановился на первом варианте

Который нарушает закон


ANS>Но при первой же необходимости вырефакторил бы в третий вариант.


А как определить, что появилась необходимость?
Re[3]: Закон Деметера на практике
От: DrDred Россия  
Дата: 18.02.07 10:51
Оценка: 1 (1)
Здравствуйте, Mikhail Polykovsky, Вы писали:

MP>Я правильно понял, что это за третий вариант?

Да... Просто так имхо было чуть покрасивше, ибо не нужно было привязываться к методам вида getLeftFrontWeel()
... << RSDN@Home 1.2.0 alpha rev. 672>>
--
WBR, Alexander
Re[3]: Закон Деметера на практике
От: DrDred Россия  
Дата: 18.02.07 10:51
Оценка: 1 (1) +1
Здравствуйте, Mikhail Polykovsky, Вы писали:

ANS>>Но при первой же необходимости вырефакторил бы в третий вариант.


MP>А как определить, что появилась необходимость?

По-моему а Фаулера в "Рефакторинге" была мысль насчет 3-х использований, то есть первый раз мы просто пишем как есть, если мы проходим по этому коду второй раз, и он нам не нравится, мы помечаем его как потенциального кандидата на рефакторинг, ну аж если мы попали сюда в третий раз...
На самом деле все смотрится по обстоятельствам, и думаю можно вполне доверять своему чутью..
... << RSDN@Home 1.2.0 alpha rev. 672>>
--
WBR, Alexander
Re[3]: Закон Деметера на практике
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 18.02.07 11:02
Оценка: 2 (1)
Здравствуйте, Mikhail Polykovsky, Вы писали:


ANS>>При именно такой постановке задания, я бы остановился на первом варианте


MP>Который нарушает закон


Хм. Криво посотрел на код Я за такой вариант:
Машина.обслуживание() {
   колёса.накачать()
}

И никакого нарушения.

ANS>>Но при первой же необходимости вырефакторил бы в третий вариант.


MP>А как определить, что появилась необходимость?


При помощи сдравого смысла. Кстати, я и тут не точно выразился Я говорил о рефакторинге не в твой третий вариант, а в вариант когда колёса накачивает некий "Насос". То есть, "ОбслуживающийПерсонал" получает у "Машины" колёса и передаёт их "Насосу" для накачки. Но плодить такое без надобности я бы не стал. Начал бы с кода, который привёл выше.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re: Закон Деметера на практике
От: GlebZ Россия  
Дата: 18.02.07 11:51
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

MP>Как вы считаете, какой из вариантов более аккуратный? Какой облегчает поддержку и дальшейшие изменения программы? Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?

По жизни — это шашечки. Концептуально и логически, в данном контексте, верен третий вариант. Надуть колеса — это один из методов обслуживания. Колесо само по себе не надувается.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Re[4]: Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 12:25
Оценка:
ANS> При помощи сдравого смысла. Кстати, я и тут не точно выразился Я говорил о рефакторинге не в твой третий вариант, а в вариант когда колёса накачивает некий "Насос". То есть, "ОбслуживающийПерсонал" получает у "Машины" колёса и передаёт их "Насосу" для накачки. Но плодить такое без надобности я бы не стал. Начал бы с кода, который привёл выше.

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

Например, есть список Машин, у Машин есть Колеса, у Колес есть МаркаКолеса. Чтобы сделать Отчет по МаркамКолес, нужно перебрать все Машины, обратиться к Колесам, получить МаркуКолеса и вывести в Отчет. В такой терминологии как ты распределишь вызовы по методам?
Re[2]: Закон Деметера на практике
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 18.02.07 12:26
Оценка: 6 (2) +1
Здравствуйте, GlebZ, Вы писали:

MP>> Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?

GZ>По жизни — это шашечки.

Если ты подходиш с принципом — один класс решает одну задачу, то очень даже не шашечки. То есть, если класс "умеет" "качать" колёса, то не его заботой должно "извлечение" колёс из машины.

GZ>Концептуально и логически, в данном контексте, верен третий вариант. Надуть колеса — это один из методов обслуживания. Колесо само по себе не надувается.


Имхо, соотносить один в один объекты реального мира и в программе это большая ошибка.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[2]: Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 12:27
Оценка:
MP>>Как вы считаете, какой из вариантов более аккуратный? Какой облегчает поддержку и дальшейшие изменения программы? Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?
GZ>По жизни — это шашечки. Концептуально и логически, в данном контексте, верен третий вариант. Надуть колеса — это один из методов обслуживания. Колесо само по себе не надувается.

Если шашечки, почему тогда не первый вариант? Он на один метод меньше, значит проще.
Re[3]: Принципы выбора объектов
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 12:32
Оценка:
ANS>Имхо, соотносить один в один объекты реального мира и в программе это большая ошибка.

Почему? Вроде бы ООП на этом построено?
Re[4]: Принципы выбора объектов
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 18.02.07 12:57
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

ANS>>Имхо, соотносить один в один объекты реального мира и в программе это большая ошибка.


MP>Почему? Вроде бы ООП на этом построено?


ООП построен на том, что объекты в программе (а не в реальном мире) обмениваются сообщениями. В "Философии" об этом много философствовали
Автор: Andrei N.Sobchuck
Дата: 15.06.06
, так что тут не то место чтобы спорить на эту тему.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[5]: Закон Деметера на практике
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 18.02.07 12:57
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:


MP>Например, есть список Машин, у Машин есть Колеса, у Колес есть МаркаКолеса. Чтобы сделать Отчет по МаркамКолес, нужно перебрать все Машины, обратиться к Колесам, получить МаркуКолеса и вывести в Отчет. В такой терминологии как ты распределишь вызовы по методам?


Если бы мне в одном месте, по ходу дела, понадобилось бы отобрать машины по марке колеса, я бы, скорее всего, не заморачивался с законом Деметера. А вот если уже в двух (или в трёх ), то скорее всего создал бы класс "Фильтр", который умеет фильтровать машины по некому "Критерию".
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[6]: Закон Деметера на практике
От: Mikhail Polykovsky Россия  
Дата: 18.02.07 13:11
Оценка:
MP>>Например, есть список Машин, у Машин есть Колеса, у Колес есть МаркаКолеса. Чтобы сделать Отчет по МаркамКолес, нужно перебрать все Машины, обратиться к Колесам, получить МаркуКолеса и вывести в Отчет. В такой терминологии как ты распределишь вызовы по методам?

ANS>Если бы мне в одном месте, по ходу дела, понадобилось бы отобрать машины по марке колеса, я бы, скорее всего, не заморачивался с законом Деметера. А вот если уже в двух (или в трёх ), то скорее всего создал бы класс "Фильтр", который умеет фильтровать машины по некому "Критерию".


Ну хорошо, пусть Фильтр. Покажи мне пожалуйста, как бы ты сделал метод отбора Машин по МаркеКолеса в классе Фильтр. Метод получает на вход массив объектов "Машина".
Re[3]: Закон Деметера на практике
От: GlebZ Россия  
Дата: 18.02.07 16:49
Оценка: 1 (1)
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Если ты подходиш с принципом — один класс решает одну задачу, то очень даже не шашечки. То есть, если класс "умеет" "качать" колёса, то не его заботой должно "извлечение" колёс из машины.

По жизни может быть что и класс колесо может быть избыточным, и есть неотъемлимая часть машины.

ANS>Имхо, соотносить один в один объекты реального мира и в программе это большая ошибка.

А тут нет реального мира. Объект обслуживание — может не быть объектом реального мира, а эту работу выполняет водитель. И сущность водитель или станция тех. обслуживания мы можем не учитывать. Обслуживание — это может быть синт. абстракция созданная для достижения целей программы.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Re[3]: Закон Деметера на практике
От: GlebZ Россия  
Дата: 18.02.07 17:05
Оценка: 12 (3)
Здравствуйте, Mikhail Polykovsky, Вы писали:

MP>Если шашечки, почему тогда не первый вариант? Он на один метод меньше, значит проще.

Мы же смотрим именно концептуальную целостность гипотетической задачи. Если мы выделили сущность "обслуживание", значит это конгломерат некоторых действий обслуживающих сценарии связнные с обслуживанием. У нас есть три сущности — "машина", "колесо", "обслуживание" или объект занимающийся обслуживанием. У нас есть некоторые сценарий, в число которых не входит вариант надуть машину. Соответвенно машина тут лишняя. Остается обслуживание и колесо, и два возможных варианта:
1. колесо.надуть(обслуживание объект_обслуживания)
2. обслуживание.надуть(колесо объект_колесо).
Любой из этих вариантов будет верным, но второй вариант в большинстве случаев будет вернее. Обслуживание может инициировать надувание колеса, колесо — нет. Обслуживание может объединить сценарии связанные с обслуживанием все мащины (или можем даже выделить сущность "обслуживание колеса".
... << RSDN@Home 1.2.0 alpha rev. 0>>
Re[4]: Принципы выбора объектов
От: GlebZ Россия  
Дата: 18.02.07 17:09
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

ANS>>Имхо, соотносить один в один объекты реального мира и в программе это большая ошибка.


MP>Почему? Вроде бы ООП на этом построено?

Уже давно нет. Мы делаем синтетическую задачу в совершенно другом окружении в виде компьютера. Поэтому программа должна строится на основе действий(сценариев) пользователя при общении с компьютером. А действия бывает иногда противоречат объектам реального мира, или объекты реального мира могут быть слишком усложнены для реализации на компьютере.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Re[7]: Закон Деметера на практике
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 19.02.07 09:07
Оценка: 2 (1)
Здравствуйте, Mikhail Polykovsky, Вы писали:

MP>Ну хорошо, пусть Фильтр. Покажи мне пожалуйста, как бы ты сделал метод отбора Машин по МаркеКолеса в классе Фильтр. Метод получает на вход массив объектов "Машина".


Тут есть два способа.
1-й. Сделать Фильтр, который получает на вход список машин, у которых получает список колёс и сам определяет есть ли колёса, удовлетворяющие условию:
 колёса := машина.всеКолёса().
 foreach (колесо: колёса) {
  if (колесо.марка = искомаяМарка) return true;
 }


2-й. Сделать ФильтрМашин, который получает на вход список машин, вытягует у каждой машины список колёс и передаёт уже этот список колёс на проверку ФильтруКолёс:
 колёса := машина.всеКолёса().
 фильтрКолёс = new ФильтрКолёс(искомаяМарка).
 return фильтрКолёс.проверить(колёса).


В первом случае ты можеш фильтровать только машины. Во втором, можешь машины, и можеш колёса (например на складе). Объём кода почти одинаков (понятно, что во втором случае больше инфраструктуры — типа конструкторов и пр.). Первый случай нарушает Закон Деметера, второй нет. Выбор за тобой.

ЗЫ. Работающие программы получаются и без следования Закону Деметера Просто его нарушение может быть звоночком, что есть и более удачные варианты.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.