Закон Деметры
От: AlexRK  
Дата: 19.07.13 07:58
Оценка: -1
Уважаемые форумчане. Интересует такой вопрос.

Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?

Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void).
Ибо если мы вернем что-то, то мы с этим "что-то" не имеем права ничего делать. Выходит, что, к примеру, все фабрики объектов идут лесом.

С другой стороны, мы можем возвращаемое значение записать в поле и тут же у этого поля вызвать метод — это вроде как не будет нарушением закона Деметры. Но выглядит это очень тупо.

Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

Предвижу возможные ответы типа "закон Деметры — это не закон, а рекомендация, надо применять его разумно, .....".
Такая позиция меня не интересует, интересно именно _формальное_ определение, если оно конечно существует. А если не существует, то интересны любые мнения по поводу того, каким оно могло бы быть.
Re: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.07.13 18:01
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Уважаемые форумчане. Интересует такой вопрос.


ARK>Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?

Давно думал подискутировать на тему Law of Demeter. Но как-то поводу не возникало. Нет, более формального не встречал.

ARK>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void).

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

ARK>С другой стороны, мы можем возвращаемое значение записать в поле и тут же у этого поля вызвать метод — это вроде как не будет нарушением закона Деметры. Но выглядит это очень тупо.

Согласен. Тупо. Но формально (если мы используем конкретно эти формулировки) и не всякое поле подойдет. Глобальная переменная — подойдет. Поле объекта чей метод выполняется — нет. И это еще более тупо.

ARK>Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

формально — нет. Дело в том, что оператор деления "/" обычно не метод объекта результата суммы a и b. Вообще, когда мы вызываем статический метод (а оператор деления "/" обычно можно считать именно таковым), закон Деметры неприменим.

ARK>Предвижу возможные ответы типа "закон Деметры — это не закон, а рекомендация, надо применять его разумно, .....".

ARK>Такая позиция меня не интересует, интересно именно _формальное_ определение, если оно конечно существует. А если не существует, то интересны любые мнения по поводу того, каким оно могло бы быть.
Все верно, у "ЗАКОН"-а должна быть очень формальная формулировка, которая не допускает кривотолков и разночтений. ИМХО, никаких "придумай себе сам, когда это применять" не допустимо.

По поводу мнения, каким бы оно могло быть: думал по этому поводу. Некое рациональное зерно присутствует, но в форме "делай только так и будет счастье" слишком много иррационального. Плодить водопроводные методы a.BMethod() вместо a.b.Method() не видно вообще никакого резона. Особенно если это a.Name.Substring(...) или что-то вроде. Не делать же метод A.NameSubstring(...).
Re[2]: Закон Деметры
От: AlexRK  
Дата: 19.07.13 18:24
Оценка:
Здравствуйте, samius, Вы писали:

S>Давно думал подискутировать на тему Law of Demeter. Но как-то поводу не возникало.


Ну вот, повод нашелся.

ARK>>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void).

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

А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".
(Аналогично можно и просто присвоить объект в локальную переменную, а потом дернуть метод у этой переменной.)
Но по идее-то LoD, как я его понимаю, запрещает нам оперировать внутренней структурой объекта _в принципе_ (а не только в текущем методе).

ARK>>С другой стороны, мы можем возвращаемое значение записать в поле и тут же у этого поля вызвать метод — это вроде как не будет нарушением закона Деметры. Но выглядит это очень тупо.

S>Согласен. Тупо. Но формально (если мы используем конкретно эти формулировки) и не всякое поле подойдет. Глобальная переменная — подойдет. Поле объекта чей метод выполняется — нет. И это еще более тупо.

Почему же поле текущего объекта не подойдет? Вроде бы не видно в определении чего-то, противоречащего этому.

ARK>>Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

S>формально — нет. Дело в том, что оператор деления "/" обычно не метод объекта результата суммы a и b. Вообще, когда мы вызываем статический метод (а оператор деления "/" обычно можно считать именно таковым), закон Деметры неприменим.

Ну а если оператор таки метод объекта? Скажем, в Smalltalk или Eiffel.

Насчет статических объектов... Это по сути вырожденный случай обычных объектов, существующих в одном экземпляре. На мой взгляд, выделять их в отдельную касту не стоит.

S>Все верно, у "ЗАКОН"-а должна быть очень формальная формулировка, которая не допускает кривотолков и разночтений. ИМХО, никаких "придумай себе сам, когда это применять" не допустимо.


+1

S>По поводу мнения, каким бы оно могло быть: думал по этому поводу. Некое рациональное зерно присутствует, но в форме "делай только так и будет счастье" слишком много иррационального. Плодить водопроводные методы a.BMethod() вместо a.b.Method() не видно вообще никакого резона.


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

S>Особенно если это a.Name.Substring(...) или что-то вроде. Не делать же метод A.NameSubstring(...).


А вот это уже что-то иное. Возможно, граница пролегает либо тут: "у вложенных объектов можно дергать только чистые функции", либо тут: "объект может выставлять наружу только примитивные value-типы, которые можно использовать любым способом".
Re: Закон Деметры
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 19.07.13 19:44
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?


А чем это недостаточно формально?

More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]
O itself
m's parameters
Any objects created/instantiated within m
O's direct component objects
A global variable, accessible by O, in the scope of m


ARK>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void). Ибо если мы вернем что-то, то мы с этим "что-то" не имеем права ничего делать.


Это с чего ты такое взял? Согласно определению выше мы не имеем права позвать у этого чего то методы, а вот передать его целиком в другой метод — вполне. А вот тот метод у своих параметров уже имеет право звать что угодно.

С другой стороны, этот самый закон Деметры крайне спорная и стремная вещь, слишком грубые и негибкие ограничения он налагает.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Закон Деметры
От: AlexRK  
Дата: 19.07.13 20:14
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>А чем это недостаточно формально?

AVK>

AVK>More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]
AVK>O itself
AVK>m's parameters
AVK>Any objects created/instantiated within m
AVK>O's direct component objects
AVK>A global variable, accessible by O, in the scope of m


Многим. К примеру, я помещаю результат функции в поле объекта и вызываю уже у поля метод. Я нарушаю закон или нет? Из определения неясно.
Что такое "created within"? Результат (2 + 3) считается "created within" или нет?

ARK>>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void). Ибо если мы вернем что-то, то мы с этим "что-то" не имеем права ничего делать.


AVK>Это с чего ты такое взял? Согласно определению выше мы не имеем права позвать у этого чего то методы, а вот передать его целиком в другой метод — вполне. А вот тот метод у своих параметров уже имеет право звать что угодно.


Ну, в принципе да, просто тогда смысл закона не особо понятен. Как я писал в посте выше, по идее закон должен запрещать работать непосредственно с внутренней структурой объекта. Это также утверждается в книгах Clean Code и The Pragmatic Programmer, а также косвенным образом указано в начале статьи в википедии. А если мы просто перенесем операцию в другое место в нашем же объекте — это хрень какая-то получается.

AVK>С другой стороны, этот самый закон Деметры крайне спорная и стремная вещь, слишком грубые и негибкие ограничения он налагает.


Существуют и другие мнения (в том числе экспертов в Computer Science).
Re: Закон Деметры
От: Abyx Россия  
Дата: 19.07.13 20:47
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>Уважаемые форумчане. Интересует такой вопрос.


ARK>Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?


зачем Вам формальное определение если Вы не понимаете зачем LoD нужен.

суть этого закона в том что нельзя обращаться к зависимости своей зависимости.
т.е. если A зависит от B, а B зависит от C, то не надо добавлять зависимость A->C.

ARK>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void).

ARK>Ибо если мы вернем что-то, то мы с этим "что-то" не имеем права ничего делать. Выходит, что, к примеру, все фабрики объектов идут лесом.
зависит от того, является ли фабрика методом объекта который она создает, т.е. будет новая зависимость или нет.
если является — это подпадает под "Any objects created/instantiated within m" (конструктор это тоже фабрика)

ARK>С другой стороны, мы можем возвращаемое значение записать в поле и тут же у этого поля вызвать метод — это вроде как не будет нарушением закона Деметры. Но выглядит это очень тупо.

да, *Вы* можете взять собаку за ногу, и попробовать ей подвигать.

ARK>Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

оператор деления этот метод (операция) класса числа, очевидно же, не?

ARK>Предвижу возможные ответы типа "закон Деметры — это не закон, а рекомендация, надо применять его разумно, .....".

ARK>Такая позиция меня не интересует
и это неразумно.
In Zen We Trust
Re[2]: Закон Деметры
От: AlexRK  
Дата: 19.07.13 21:03
Оценка:
Здравствуйте, Abyx, Вы писали:

ARK>>Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?


A>зачем Вам формальное определение если Вы не понимаете зачем LoD нужен.


Я понимаю, зачем он нужен. Если бы вы удосужились прочесть другие мои посты в этой теме, то такое предположение у вас бы вряд ли возникло.

A>суть этого закона в том что нельзя обращаться к зависимости своей зависимости.

A>т.е. если A зависит от B, а B зависит от C, то не надо добавлять зависимость A->C.

Да, я в курсе.

ARK>>Потому что если трактовать википедийное определение буквально, то получается, что у объектов допустимы только функции, не возвращающие значений (void).

ARK>>Ибо если мы вернем что-то, то мы с этим "что-то" не имеем права ничего делать. Выходит, что, к примеру, все фабрики объектов идут лесом.
A>зависит от того, является ли фабрика методом объекта который она создает, т.е. будет новая зависимость или нет.
A>если является — это подпадает под "Any objects created/instantiated within m" (конструктор это тоже фабрика)

Прошу прощения, не понял. Если фабрика не конструктор, то она идет лесом?

ARK>>С другой стороны, мы можем возвращаемое значение записать в поле и тут же у этого поля вызвать метод — это вроде как не будет нарушением закона Деметры. Но выглядит это очень тупо.

A>да, *Вы* можете взять собаку за ногу, и попробовать ей подвигать.

Т.е. это не нарушение закона? Тогда в чем его смысл, если зависимость "через слой" не устраняется?

ARK>>Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

A>оператор деления этот метод (операция) класса числа, очевидно же, не?

Не.
Как насчет Eiffel (там нет статических методов)?

ARK>>Предвижу возможные ответы типа "закон Деметры — это не закон, а рекомендация, надо применять его разумно, .....".

ARK>>Такая позиция меня не интересует
A>и это неразумно.

Можно было с этого начать — и этим закончить.
Меня интересует строгая формализация (или доказательство ее невозможности).
Re[3]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.07.13 04:55
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, samius, Вы писали:


ARK>Ну вот, повод нашелся.

Угу

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


ARK>А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".

Именно в этом и смысл, что "per rectum" можно, а намрямую — нет. Правда, тут еще требуется разобраться где проходит грань между "per rectum" и прямым подходом. Допустим
var foo = GetFoo();
foo.Bar(); // Нельзя, слишком прямо
FooBar(foo); // Можно, ибо Per rectum

Но если вспомнить что метод Foo::Bar по сути есть статический метод, принимающий неявный параметр типа Foo, то мы получаем что foo.Bar() есть ни что иное как Foo::Bar(foo), и foo.Bar() лишь синтаксический сахар. Во всяком случае для некого подмножества языков. С виртуальными методами чуть сложнее, ну да фиг с ними пока.
В итоге смысл закона сводится к тому что в контексте метода m FooBar(foo) лучше чем Foo::Bar(foo). И не совсем очевидно, чем это лучше с позиций знания внутреннего устройства класса Foo.

ARK>(Аналогично можно и просто присвоить объект в локальную переменную, а потом дернуть метод у этой переменной.)

Нет, формально присвоение локальной переменной не разрешает дергать этот метод. А в глобальную — разрешает.
ARK>Но по идее-то LoD, как я его понимаю, запрещает нам оперировать внутренней структурой объекта _в принципе_ (а не только в текущем методе).
Проблема в том, что именно формальные требования закона запрещает оперировать внутренней структурой объекта лишь в определенных случаях. И передача объекта в глобальную переменную или в сторонний метод фактически снимают это ограничение. Неформально мы можем искать и находить в законе любые смыслы, но формальное его определение сводит его к абсурду.

S>>Согласен. Тупо. Но формально (если мы используем конкретно эти формулировки) и не всякое поле подойдет. Глобальная переменная — подойдет. Поле объекта чей метод выполняется — нет. И это еще более тупо.


ARK>Почему же поле текущего объекта не подойдет? Вроде бы не видно в определении чего-то, противоречащего этому.

Я не вижу в формальном описании ничего о поле текущего объекта
Правда, там есть пункт 4. o's direct component objects, но смысл этого пункта ускользает от меня в контексте этого закона. Если он о полях объекта o', то почему было не написать об этом прямо, как в пункте 5 о глобальных переменных?

ARK>>>Еще непонятный момент с операторами. Например, в выражении "(a + b) / 3" оператор деления уже нарушает сабж.

S>>формально — нет. Дело в том, что оператор деления "/" обычно не метод объекта результата суммы a и b. Вообще, когда мы вызываем статический метод (а оператор деления "/" обычно можно считать именно таковым), закон Деметры неприменим.

ARK>Ну а если оператор таки метод объекта? Скажем, в Smalltalk или Eiffel.

в таком случае — да, нарушаем.

ARK>Насчет статических объектов... Это по сути вырожденный случай обычных объектов, существующих в одном экземпляре. На мой взгляд, выделять их в отдельную касту не стоит.

Может и не стоит, но LoD ставит их как раз отдельной кастой. Раз статический объект не может быть "получен" и "передан", его методы можно вызывать без каких-либо ограничений, невзирая на соображения о знаниях на счет "внутреннего устройства" такого объекта.

ARK>Некий резон на самом деле имеется. В первом случае у нас контролируемое изменение объекта, о котором этот объект знает. Во втором мы лезем грязными руками внутрь и можем наломать дров. Видел жуткий говнокод (если конкретно, то во фреймворке MapXtreme), когда объект подписывался на изменения своих собственных полей на разных уровнях вложенности. Это мега-жесть.

Как раз LoD не запрещает такую мега-жесть. Со своими собственными методами все что угодно.

S>>Особенно если это a.Name.Substring(...) или что-то вроде. Не делать же метод A.NameSubstring(...).


ARK>А вот это уже что-то иное. Возможно, граница пролегает либо тут: "у вложенных объектов можно дергать только чистые функции", либо тут: "объект может выставлять наружу только примитивные value-типы, которые можно использовать любым способом".

Мне не кажется что тут. В тот момент, когда мы разрабатываем публичный интерфейс некого объекта, мы уже решаем, какие знания об объекте будут лишними для использования извне. Здесь мы руководствуемся минимальностью, полнотой, непротиворечивостью, стабильностью публичного интерфейса, принципами инкапсуляции (в широком смысле слова как дуализмом абстракции). Уже на этом этапе при удачном проектировании из объекта не должно торчать то, что сделает его клиентов зависимым от деталей реализации объекта.
А LoD как раз безотносителен соображений успешности проектирования интерфейса объекта (и частоты его использования) и относителен лишь того, каким образом был получен объект внутри метода (параметром ли, инстанциирован ли напрямую, взят ли из глобальной переменной, либо иным способом).
Печально так же что прямое следование LoD ничего от нас не требует в отношении проектирования, т.к. исправление формального нарушения LoD тривиально без каких либо изменений в дизайне используемых компонент. Побочным эффектом появляется куча временных полей (которые могли бы быть локальными) и делегирующих методов.
Re[2]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.07.13 05:13
Оценка:
Здравствуйте, Abyx, Вы писали:

ARK>>Есть ли в природе более формальное определение сабжа, нежели приведенное в википедии (http://en.wikipedia.org/wiki/Law_of_Demeter)?


A>зачем Вам формальное определение если Вы не понимаете зачем LoD нужен.


A>суть этого закона в том что нельзя обращаться к зависимости своей зависимости.

A>т.е. если A зависит от B, а B зависит от C, то не надо добавлять зависимость A->C.
Из формального критерия LoD это как раз не следует. Введение глобальной переменной разрешает любые зависимости.
Re[4]: Закон Деметры
От: AlexRK  
Дата: 20.07.13 07:36
Оценка:
Здравствуйте, samius, Вы писали:

ARK>>А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".

S>Именно в этом и смысл, что "per rectum" можно, а намрямую — нет.

Ну, если рассматривать чисто эти 5 пунктов определения, то да, они допускают и такую трактовку.
Хотя там же написано: "If the law is followed, only object B knows its own internal structure".
Да и во всех других обсуждениях пишут аналогичное, например тут: http://c2.com/cgi/wiki?LawOfDemeter

S>Но если вспомнить что метод Foo::Bar по сути есть статический метод, принимающий неявный параметр типа Foo, то мы получаем что foo.Bar() есть ни что иное как Foo::Bar(foo), и foo.Bar() лишь синтаксический сахар. Во всяком случае для некого подмножества языков. С виртуальными методами чуть сложнее, ну да фиг с ними пока.


Мало того, в таком случае получается, что мы можем вызывать любые цепочки методов — мы ведь просто вызываем статические методы класса в засахаренном виде. Определение становится бессмысленным. Значит в нем явно чего-то не хватает.

S>В итоге смысл закона сводится к тому что в контексте метода m FooBar(foo) лучше чем Foo::Bar(foo). И не совсем очевидно, чем это лучше с позиций знания внутреннего устройства класса Foo.


С этих позиций ничем не лучше. Просто странный способ укорачивания метода.

ARK>>(Аналогично можно и просто присвоить объект в локальную переменную, а потом дернуть метод у этой переменной.)

S>Нет, формально присвоение локальной переменной не разрешает дергать этот метод. А в глобальную — разрешает.

Ну что же, локальные переменные LoD вообще запрещает использовать?
Про них ничего не написано, да, но что-то с ними можно делать?

ARK>>Но по идее-то LoD, как я его понимаю, запрещает нам оперировать внутренней структурой объекта _в принципе_ (а не только в текущем методе).

S>Проблема в том, что именно формальные требования закона запрещает оперировать внутренней структурой объекта лишь в определенных случаях. И передача объекта в глобальную переменную или в сторонний метод фактически снимают это ограничение. Неформально мы можем искать и находить в законе любые смыслы, но формальное его определение сводит его к абсурду.

Ну, если уж подходить с позиций буквоедства, то в определении не сказано, что мы имеем право присваивать глобальные переменные и передавать объекты в другие методы (так же, как не сказано про поля объекта). Вызывать методы у глобальных переменных можем, да. А присваивать — вовсе не факт.
Так что формальное определение не то что сводит к абсурду, оно просто неполное и допускает разные трактовки.

S>Я не вижу в формальном описании ничего о поле текущего объекта

S>Правда, там есть пункт 4. o's direct component objects, но смысл этого пункта ускользает от меня в контексте этого закона. Если он о полях объекта o', то почему было не написать об этом прямо, как в пункте 5 о глобальных переменных?

Про это написал выше.

ARK>>Ну а если оператор таки метод объекта? Скажем, в Smalltalk или Eiffel.

S>в таком случае — да, нарушаем.

Тут можно конечно высосать из пальца "трактовку", что "2 + 3" равносильно созданию объекта в текущем методе. Правда, тогда то же самое можно сказать о любых значениях, которые были возвращены вызванными методами.

ARK>>Насчет статических объектов... Это по сути вырожденный случай обычных объектов, существующих в одном экземпляре. На мой взгляд, выделять их в отдельную касту не стоит.

S>Может и не стоит, но LoD ставит их как раз отдельной кастой. Раз статический объект не может быть "получен" и "передан", его методы можно вызывать без каких-либо ограничений, невзирая на соображения о знаниях на счет "внутреннего устройства" такого объекта.

Опять же, строго говоря, в определении про это ничего нет. Ни в один из 5 пунктов статические объекты не помещаются. Не факт, что мы должны их по умолчанию разрешить, может быть наоборот?

ARK>>Некий резон на самом деле имеется. В первом случае у нас контролируемое изменение объекта, о котором этот объект знает. Во втором мы лезем грязными руками внутрь и можем наломать дров. Видел жуткий говнокод (если конкретно, то во фреймворке MapXtreme), когда объект подписывался на изменения своих собственных полей на разных уровнях вложенности. Это мега-жесть.

S>Как раз LoD не запрещает такую мега-жесть. Со своими собственными методами все что угодно.

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

ARK>>А вот это уже что-то иное. Возможно, граница пролегает либо тут: "у вложенных объектов можно дергать только чистые функции", либо тут: "объект может выставлять наружу только примитивные value-типы, которые можно использовать любым способом".

S>Мне не кажется что тут. В тот момент, когда мы разрабатываем публичный интерфейс некого объекта, мы уже решаем, какие знания об объекте будут лишними для использования извне. Здесь мы руководствуемся минимальностью, полнотой, непротиворечивостью, стабильностью публичного интерфейса, принципами инкапсуляции (в широком смысле слова как дуализмом абстракции). Уже на этом этапе при удачном проектировании из объекта не должно торчать то, что сделает его клиентов зависимым от деталей реализации объекта.
S>А LoD как раз безотносителен соображений успешности проектирования интерфейса объекта (и частоты его использования) и относителен лишь того, каким образом был получен объект внутри метода (параметром ли, инстанциирован ли напрямую, взят ли из глобальной переменной, либо иным способом).

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

S>Печально так же что прямое следование LoD ничего от нас не требует в отношении проектирования, т.к. исправление формального нарушения LoD тривиально без каких либо изменений в дизайне используемых компонент. Побочным эффектом появляется куча временных полей (которые могли бы быть локальными) и делегирующих методов.


Появление делегирующих методов в самом объекте местами неизбежно, да. И если их окажется слишком много, то это показатель того, что что-то не так.
А про временные поля в "Чистом коде" Мартина утверждается, что это хорошо — главное чтобы класс не был большим и не имел много обязанностей.
Re[3]: Закон Деметры
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 20.07.13 08:25
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Многим. К примеру, я помещаю результат функции в поле объекта и вызываю уже у поля метод. Я нарушаю закон или нет?


Да. А почему ты его не можешь нарушать? "direct component objects" это составная часть объекта, где экземпляр создается инициализатором поля и в конструкторе. Если ты создаешь объект в вызове метода, то это не direct component objects нифига.

ARK>Что такое "created within"?


Метод является владельцем экземпляра объекта.

ARK> Результат (2 + 3) считается "created within" или нет?


2 + 3 вообще не является элементом ОО дизайна в подавляющем большинстве языков, не надо доводит до абсурда.

ARK>Ну, в принципе да, просто тогда смысл закона не особо понятен.


Смысл простой. Как и чуть менее чем все правила дизайна, он обеспечивает low coupling/high cohesion. Упрощая и упорядочивая связи каждого конкретного метода мы уменьшаем связность.

ARK> Как я писал в посте выше, по идее закон должен запрещать работать непосредственно с внутренней структурой объекта.


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

ARK>А если мы просто перенесем операцию в другое место в нашем же объекте — это хрень какая-то получается.


Не просто в другое место, в другой метод. А что тебя удивляет? Многие принципы ООД по сути говорят о том, как распределить код по методам и/или классам.

ARK>Существуют и другие мнения (в том числе экспертов в Computer Science).


Да ради бога. Я исключительно свое ИМХО выражаю. Низкой связности можно и более гибкими методами добиваться, если понимать суть, а не механически следовать примитивным правилам.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[5]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 21.07.13 16:30
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, samius, Вы писали:


ARK>>>А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".

S>>Именно в этом и смысл, что "per rectum" можно, а намрямую — нет.

ARK>Ну, если рассматривать чисто эти 5 пунктов определения, то да, они допускают и такую трактовку.

ARK>Хотя там же написано: "If the law is followed, only object B knows its own internal structure".
ARK>Да и во всех других обсуждениях пишут аналогичное, например тут: http://c2.com/cgi/wiki?LawOfDemeter
Все понятно, все законы придумываются ради мира во всем мире и что бы всем сделать зашибись.
Но из формальных критериев применения следует что в некоторых случаях вызывать метод через точку вредно, а точно такой же метод но с передачей явного параметра вместо this — не вредно. Хотя методы могут быть при этом совершенно идентичные, вызывая через точку мы каким-то образом передаем знания о внутренней структуре объекта B в наш метод, а вызывая без точки — не передаем. В чем подвох, я так и не понял.

ARK>Мало того, в таком случае получается, что мы можем вызывать любые цепочки методов — мы ведь просто вызываем статические методы класса в засахаренном виде. Определение становится бессмысленным. Значит в нем явно чего-то не хватает.



S>>Нет, формально присвоение локальной переменной не разрешает дергать этот метод. А в глобальную — разрешает.


ARK>Ну что же, локальные переменные LoD вообще запрещает использовать?

ARK>Про них ничего не написано, да, но что-то с ними можно делать?
Можно, но формулировка закона касается того, каким образом был получен объект, а не того, как его хранить в локальных переменных.

ARK>Ну, если уж подходить с позиций буквоедства, то в определении не сказано, что мы имеем право присваивать глобальные переменные и передавать объекты в другие методы (так же, как не сказано про поля объекта). Вызывать методы у глобальных переменных можем, да. А присваивать — вовсе не факт.

А что может запретить записывать в глобальную переменную? Она ведь вроде по умолчанию именно "переменная" и не указано что она readonly/const.
ARK>Так что формальное определение не то что сводит к абсурду, оно просто неполное и допускает разные трактовки.
А есть ли трактовка, которая объясняет чем вызов через точку вреднее явной передачи параметра?

ARK>Тут можно конечно высосать из пальца "трактовку", что "2 + 3" равносильно созданию объекта в текущем методе. Правда, тогда то же самое можно сказать о любых значениях, которые были возвращены вызванными методами.

Фиг с ними с "2+3", хотелось бы разобраться с общим случаем 2.Add(3).Div(5)

S>>Может и не стоит, но LoD ставит их как раз отдельной кастой. Раз статический объект не может быть "получен" и "передан", его методы можно вызывать без каких-либо ограничений, невзирая на соображения о знаниях на счет "внутреннего устройства" такого объекта.


ARK>Опять же, строго говоря, в определении про это ничего нет. Ни в один из 5 пунктов статические объекты не помещаются. Не факт, что мы должны их по умолчанию разрешить, может быть наоборот?

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

S>>Как раз LoD не запрещает такую мега-жесть. Со своими собственными методами все что угодно.


ARK>Не запрещает, но делает бессмысленным (в той трактовке, в какой я его понимаю). Т.к. извне уже нельзя разрушить структуру объекта, все изменения полей идут через его методы, так что городить лапшу из подписки уже не надо — достаточно проконтролировать несколько точек входа в самом объекте.

Любая мегажесть имеет тот смысл, который вкладывает в нее автор. LoD не поощряет и не мешает мегажести. Он лишь запрещает в некоторых случаях вызывать метод через точку.

S>>Мне не кажется что тут. В тот момент, когда мы разрабатываем публичный интерфейс некого объекта, мы уже решаем, какие знания об объекте будут лишними для использования извне. Здесь мы руководствуемся минимальностью, полнотой, непротиворечивостью, стабильностью публичного интерфейса, принципами инкапсуляции (в широком смысле слова как дуализмом абстракции). Уже на этом этапе при удачном проектировании из объекта не должно торчать то, что сделает его клиентов зависимым от деталей реализации объекта.

S>>А LoD как раз безотносителен соображений успешности проектирования интерфейса объекта (и частоты его использования) и относителен лишь того, каким образом был получен объект внутри метода (параметром ли, инстанциирован ли напрямую, взят ли из глобальной переменной, либо иным способом).

ARK>Вот я считаю как раз, что определение (расширенное) должно влиять именно на структуру создаваемого объекта. Как я уже упоминал, именно это подразумевается в литературе и виденных мной обсуждениях в инете.

Да. Если мы у объекта не будем делать инстанс-методы, то применять LoD будет просто не к чему
Но все-таки закон не о том, какие объекты писать, а о том, какие методы не вызывать.

ARK>Появление делегирующих методов в самом объекте местами неизбежно, да. И если их окажется слишком много, то это показатель того, что что-то не так.

ARK>А про временные поля в "Чистом коде" Мартина утверждается, что это хорошо — главное чтобы класс не был большим и не имел много обязанностей.
Вот у Боба как раз более вменяемые критерии чистого кода. В том числе что касается знания о внутренней структуре левых объектов. Например, правило "одного уровня абстракции на функцию".

К сожалению раздел о LoD у того же Боба цитирует определение буквально и не раскрывает сути его работы.
Да, Боб добавляет неразбирихи. Оказывается, что надо делать различие между объектами и структурами данных. Знать лишнее об объектах плохо, а о структурах данных — нет.
Re[5]: Закон Деметры
От: Sinclair Россия https://github.com/evilguest/
Дата: 22.07.13 08:40
Оценка: +1
Здравствуйте, AlexRK, Вы писали:


ARK>Мало того, в таком случае получается, что мы можем вызывать любые цепочки методов — мы ведь просто вызываем статические методы класса в засахаренном виде. Определение становится бессмысленным. Значит в нем явно чего-то не хватает.


S>>В итоге смысл закона сводится к тому что в контексте метода m FooBar(foo) лучше чем Foo::Bar(foo). И не совсем очевидно, чем это лучше с позиций знания внутреннего устройства класса Foo.


ARK>С этих позиций ничем не лучше. Просто странный способ укорачивания метода.


Это способ сделать неявные зависимости явными. Раньше весь ваш код зависел от метода Bar, а теперь только код метода FooBar.
В ещё более реальном случае (типа того, который был процитирован где-то выше), мы перестаём полагаться на то, что у Customer есть Name, из которого можно сделать Substring с далеко идущими выводами, а начинаем полагаться на наличие у Customer метода GetFirstName — на этот раз, с чётко определённой семантикой.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Закон Деметры
От: Кодт Россия  
Дата: 23.07.13 10:26
Оценка: :)
Здравствуйте, samius, Вы писали:

ARK>>А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".

S>Именно в этом и смысл, что "per rectum" можно, а намрямую — нет.

Заметили вы оба или нет, но это каламбур. Per rectum и есть "напрямую".
Перекуём баги на фичи!
Re[6]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.13 12:06
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, AlexRK, Вы писали:


S>>>В итоге смысл закона сводится к тому что в контексте метода m FooBar(foo) лучше чем Foo::Bar(foo). И не совсем очевидно, чем это лучше с позиций знания внутреннего устройства класса Foo.


ARK>>С этих позиций ничем не лучше. Просто странный способ укорачивания метода.


S>Это способ сделать неявные зависимости явными. Раньше весь ваш код зависел от метода Bar, а теперь только код метода FooBar.

Это не совсем так. Предположим, что код нашего метода работает с разными экземплярами Foo. Один мы инстанциировали прямо в методе, либо взяли из глобальной переменной, другой — получили нехорошим (в контексте формулировки LoD) способом. Итак:
Foo foo1 = new Foo();
foo1.Bar(); // легально
// но
Foo foo2 = GetFoo();
foo2.Bar(); // нелегально
FooBar(foo2); // легально

Оказывается, что с переходом на вызов FooBar с передачей foo2 наш метод продолжает зависеть от метода Bar, хотя мы все сделали по формальным правилам LoD. Но еще и зависит от FooBar. Печалька.

S>В ещё более реальном случае (типа того, который был процитирован где-то выше), мы перестаём полагаться на то, что у Customer есть Name, из которого можно сделать Substring с далеко идущими выводами, а начинаем полагаться на наличие у Customer метода GetFirstName — на этот раз, с чётко определённой семантикой.

Это само по себе ничего, а вот запрет вызывать GetFirstName у экземпляров Customer, не инстанциированных прямо здесь же, доставляет.
Re[5]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.13 12:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, samius, Вы писали:


ARK>>>А в чем в таком случае смысл закона? Мы все равно вызываем нужный нам метод, просто "per rectum".

S>>Именно в этом и смысл, что "per rectum" можно, а намрямую — нет.

К>Заметили вы оба или нет, но это каламбур. Per rectum и есть "напрямую".

Это напрямую для тех, кто термин придумал. Для медиков, полагаю. А для программистов "напрямую" и "через ..опу" — это два различных направления
Re[6]: Закон Деметры
От: Кодт Россия  
Дата: 23.07.13 13:44
Оценка:
Здравствуйте, samius, Вы писали:

К>>Заметили вы оба или нет, но это каламбур. Per rectum и есть "напрямую".

S>Это напрямую для тех, кто термин придумал. Для медиков, полагаю. А для программистов "напрямую" и "через ..опу" — это два различных направления

rectum = прямая (кишка). А программисты могли бы и не использовать в речи двусмысленные термины
Перекуём баги на фичи!
Re[7]: Закон Деметры
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.07.13 04:28
Оценка:
Здравствуйте, samius, Вы писали:

S>>Это способ сделать неявные зависимости явными. Раньше весь ваш код зависел от метода Bar, а теперь только код метода FooBar.

S>Это не совсем так. Предположим, что код нашего метода работает с разными экземплярами Foo. Один мы инстанциировали прямо в методе, либо взяли из глобальной переменной, другой — получили нехорошим (в контексте формулировки LoD) способом. Итак:
S>
S>Foo foo1 = new Foo();
S>foo1.Bar(); // легально
S>// но
S>Foo foo2 = GetFoo();
S>foo2.Bar(); // нелегально
S>FooBar(foo2); // легально
S>

S>Оказывается, что с переходом на вызов FooBar с передачей foo2 наш метод продолжает зависеть от метода Bar, хотя мы все сделали по формальным правилам LoD. Но еще и зависит от FooBar. Печалька.
Не совсем так. Если я правильно понимаю закон, то получение из моего личного GetFoo() или из глобального GetFoo() ничуть не хуже вызова конструктора.
Вот если я получаю его вот так — тогда уже нелегально:

Foo foo1 = new Foo();
foo1.Bar(); // легально
// но
Foo foo2 = GetFoo();
foo2.Bar(); // легально
var foo3 = baz.Foo;
foo3.Bar(); // нелегально - я полагаюсь на то, что baz возвращает foo, который умеет Bar. 
FooBar(foo3); // легально - я полагаюсь на то, что FooBar разберётся с тем, что делать с baz.Foo


S>Это само по себе ничего, а вот запрет вызывать GetFirstName у экземпляров Customer, не инстанциированных прямо здесь же, доставляет.

Нет такого запрета. Есть запрет вызывать GetFirstname у экземпляров Customer, полученных через третьих лиц. Если его инстанцировал я, получил из моего же метода, или он пришёл в виде параметра — то можно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.07.13 04:51
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>Оказывается, что с переходом на вызов FooBar с передачей foo2 наш метод продолжает зависеть от метода Bar, хотя мы все сделали по формальным правилам LoD. Но еще и зависит от FooBar. Печалька.

S>Не совсем так. Если я правильно понимаю закон, то получение из моего личного GetFoo() или из глобального GetFoo() ничуть не хуже вызова конструктора.
Хорошо, прикинем, как foo1 из строчки Foo foo1 = GetFoo() подходит к формулировке.

More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]
1. O itself
2. m's parameters
3. Any objects created/instantiated within m
4. O's direct component objects
5. A global variable, accessible by O, in the scope of m

Почти очевидно что это не 1, не 2, не 3, и не 5. Сомнения вызывает пункт 4, но мы не знаем, каким образом GetFoo (пусть он личный или глобальный) достает экземпляр, может и от третьих лиц, тогда foo1 не является O's direct component objects. Вообще, формально что такое "direct component objects"?
Но не суть важно. Пример был о том что в теле одного метода для экземпляров полученных разным способом (легальным и нелегальным) работают разные разрешения. Точнее, о том, что запрет вызывать некоторые методы у нелегально полученных объектов освобождает от зависимости лишь в том случае, когда нет вызовов методов у легально полученных объектов. Как в этом случае влияет закон на зависимости — непонятно.

S>Вот если я получаю его вот так — тогда уже нелегально:


S>
S>Foo foo1 = new Foo();
S>foo1.Bar(); // легально
S>// но
S>Foo foo2 = GetFoo();
S>foo2.Bar(); // легально
S>var foo3 = baz.Foo;
S>foo3.Bar(); // нелегально - я полагаюсь на то, что baz возвращает foo, который умеет Bar. 
S>FooBar(foo3); // легально - я полагаюсь на то, что FooBar разберётся с тем, что делать с baz.Foo
S>

Хорошо, возьмем за основу именно этот вариант. Пусть будет baz.Foo.

S>>Это само по себе ничего, а вот запрет вызывать GetFirstName у экземпляров Customer, не инстанциированных прямо здесь же, доставляет.

S>Нет такого запрета. Есть запрет вызывать GetFirstname у экземпляров Customer, полученных через третьих лиц. Если его инстанцировал я, получил из моего же метода, или он пришёл в виде параметра — то можно.
Верно. Неудачно выразился и получилось что свел 5 критериев из формулировки к одному "не инстанциированных прямо здесь же". Конечно, работают все 5. Пусть будет order.Customer.GetFirstName().
Re[9]: Закон Деметры
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.07.13 06:37
Оценка: +2
Здравствуйте, samius, Вы писали:

S>Хорошо, прикинем, как foo1 из строчки Foo foo1 = GetFoo() подходит к формулировке.

S>

S>More formally, the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:[2]
S>1. O itself
S>2. m's parameters
S>3. Any objects created/instantiated within m
S>4. O's direct component objects
S>5. A global variable, accessible by O, in the scope of m

S>Почти очевидно что это не 1, не 2, не 3, и не 5. Сомнения вызывает пункт 4, но мы не знаем, каким образом GetFoo (пусть он личный или глобальный) достает экземпляр, может и от третьих лиц, тогда foo1 не является O's direct component objects.
Ага, плохо подходит.
S>Вообще, формально что такое "direct component objects"?
Объекты, включённые в O. В нарочито-ссылочных языках типа Delphi, Java, или C#, отличить объекты "принадлежащие" О от внешних ссылок формально невозможно, но в данном контексте это не очень важно.

S>Но не суть важно. Пример был о том что в теле одного метода для экземпляров полученных разным способом (легальным и нелегальным) работают разные разрешения. Точнее, о том, что запрет вызывать некоторые методы у нелегально полученных объектов освобождает от зависимости лишь в том случае, когда нет вызовов методов у легально полученных объектов. Как в этом случае влияет закон на зависимости — непонятно.

Как раз понятно. Мы имеем зависимости двух типов:
1. Я завишу от foo; я завишу от baz
2. Я завишу от того, что foo зависит от baz.
Закон борется с зависимостями второго типа. Его выполнение гарантирует то, что если я изменил зависимость между foo и baz, то список потенциальных повреждений ограничивается прямыми зависимостями от foo. Так мы локализуем возможные проблемы.

S>Верно. Неудачно выразился и получилось что свел 5 критериев из формулировки к одному "не инстанциированных прямо здесь же". Конечно, работают все 5. Пусть будет order.Customer.GetFirstName().

Вот так нельзя. Мало ли, как устроен кастомер с точки зрения ордера. Завтра мы захотим иметь возможность работать с юридическими лицами, и весь код, завязанный на личное имя покупателя, сломается.
Закон Деметры заставляет вас задуматься над тем, зачем вообще нужна эта строчка.
Чтобы написать в емейле "Уважаемый <firstName>, ваш заказ номер <orderNumber> уже отправлен, и в самое ближайшее время заслышится жизнерадостный стук в вашу дверь или весёлая трель домофона"?
Тогда надо делать order.GetCustomerGreetingLine(), который для юридических лиц сможет вернуть "представитель ООО Мефодий Мунтян и Сыновья, действующий на основании доверенности №2013/156Ф, выданной 5 июня 2013 года". Независимо от того, каким боком Order привязан к Customer.
Примерно так.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.