Закон Деметры
От: 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.
Примерно так.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 08:12
Оценка:
Здравствуйте, AndrewVK, Вы писали:

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


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

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

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

А метод другого объекта не может передать владение (абстрактная фабрика)?

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


Ну, меня интересует формальное определение закона (что указано в первом посте), если конечно оно в принципе возможно. Формальное определение должно учитывать все случаи, в том числе и арифметические операторы. Абсурд тут ни при чем. Кроме того, есть ведь языки и не принадлежащие "большинству".

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

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

А что, перенос вызова в другой метод нашего объекта уменьшает число связей? Если мы используем напрямую Order.Customer (неважно, в каком методе), то по-прежнему, если кто-то изменит реализацию Customer, нам придется переделывать свой код. Нет?
Re[6]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 08:27
Оценка:
Здравствуйте, samius, Вы писали:

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

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

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

ARK>>Так что формальное определение не то что сводит к абсурду, оно просто неполное и допускает разные трактовки.

S>А есть ли трактовка, которая объясняет чем вызов через точку вреднее явной передачи параметра?

Если явная передача идет в метод нашего же объекта, то мне такая трактовка неизвестна — разницы в этих двух случаях не вижу, по-прежнему мы завязаны на структуру другого объекта (потенциально подверженного изменениям).

S>Но все-таки закон не о том, какие объекты писать, а о том, какие методы не вызывать.


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

S>К сожалению раздел о LoD у того же Боба цитирует определение буквально и не раскрывает сути его работы.


Да, там все основывается на чутье и опыте. А хотелось бы больше формализма.

S>Да, Боб добавляет неразбирихи. Оказывается, что надо делать различие между объектами и структурами данных. Знать лишнее об объектах плохо, а о структурах данных — нет.


Да, есть там такое. Это не очень понятно, ведь изменение в структуре данных точно так же потребует изменений в клиентском коде.
Re[6]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 08:32
Оценка:
Здравствуйте, Sinclair, Вы писали:

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



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


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


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


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

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

Ну да, я про это и говорю.

Комментируя фразу "в контексте метода m FooBar(foo) лучше чем Foo::Bar(foo)", я подумал, что речь идет исключительно о разнице между статическим методом и методом экземпляра (возможно, я не так понял ее смысл).
Re[7]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 08:34
Оценка:
Здравствуйте, Кодт, Вы писали:

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


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

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

К>rectum = прямая (кишка). А программисты могли бы и не использовать в речи двусмысленные термины


Скажите лучше, что вы думаете о законе Деметры.
После чтения ваших постов у меня сложилось впечатление, что вы интересуетесь всякими теоретическими изысканиями.
Re[10]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 08:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вот так нельзя. Мало ли, как устроен кастомер с точки зрения ордера. Завтра мы захотим иметь возможность работать с юридическими лицами, и весь код, завязанный на личное имя покупателя, сломается.

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

Полностью согласен.

Но в формальном определении этого нет. Как сюда втиснуть фабрики объектов и операторы? Никак, только "полагаясь на здравый смысл"?
Re[10]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.07.13 09:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


S>>Вообще, формально что такое "direct component objects"?

S>Объекты, включённые в O. В нарочито-ссылочных языках типа Delphi, Java, или C#, отличить объекты "принадлежащие" О от внешних ссылок формально невозможно, но в данном контексте это не очень важно.
В данном — да, не очень важно. Но вообще в контексте LoD — очень важно. Ведь нам надо точно знать, у каких объектов (даже экземпляров) какие методы можно звать.

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

S>Как раз понятно. Мы имеем зависимости двух типов:
S>1. Я завишу от foo; я завишу от baz
S>2. Я завишу от того, что foo зависит от baz.
Отлично. Обратите внимание, что ваши типы зависимостей не зависят от способа получения объекта, как в LoD.

S>Закон борется с зависимостями второго типа. Его выполнение гарантирует то, что если я изменил зависимость между foo и baz, то список потенциальных повреждений ограничивается прямыми зависимостями от foo. Так мы локализуем возможные проблемы.

Прежде мы вынуждены разобраться с тем, чем зависимость от метода foo.Bar() хуже зависимости от метода FooBar(foo). И чем потенциальные повреждения в одном случае хуже потенциальных повреждений в другом.

Из формулировки закона можно сделать весьма сомнительное следствие, говорящее о том что вызов через точку — зло. Ведь если писать только методы с явной передачей параметра (я не рассматриваю сейчас виртуальные методы), то применять LoD будет просто не к чему.

S>>Пусть будет order.Customer.GetFirstName().

S>Вот так нельзя. Мало ли, как устроен кастомер с точки зрения ордера. Завтра мы захотим иметь возможность работать с юридическими лицами, и весь код, завязанный на личное имя покупателя, сломается.
S>Закон Деметры заставляет вас задуматься над тем, зачем вообще нужна эта строчка.
S>Чтобы написать в емейле "Уважаемый <firstName>, ваш заказ номер <orderNumber> уже отправлен, и в самое ближайшее время заслышится жизнерадостный стук в вашу дверь или весёлая трель домофона"?
S>Тогда надо делать order.GetCustomerGreetingLine(), который для юридических лиц сможет вернуть "представитель ООО Мефодий Мунтян и Сыновья, действующий на основании доверенности №2013/156Ф, выданной 5 июня 2013 года". Независимо от того, каким боком Order привязан к Customer.
S>Примерно так.
Все как бы верно. Только причиной создания метода GetCustomerGreetingLine стал не LoD, а уточнение требований (или оверинженеринг). Вчера было достаточно GetFirstName-а без учета представителей и доверенностей. И опять таки, по тому же LoD метод order.GetCustomerGreetingLine() дает меньшую гибкость чем Order::GetCustomerGreetingLine(order).
Re[5]: Закон Деметры
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 24.07.13 09:32
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Это уже некоторая политика владения объектом.


Это смысл фразы direct component objects.

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

AVK>>Метод является владельцем экземпляра объекта.
ARK>А метод другого объекта не может передать владение (абстрактная фабрика)?

Фабрика этот тот же конструктор, вид сбоку.

ARK>Ну, меня интересует формальное определение закона


Пока что я вижу что тебя интересует доведение до абсурда, имхо. В дизайне нет и не может быть законов, которые формализуются до уровня математических теорем, потому что почти все они это эмпирика, а не выведенные математически правила. А эмпирика потому что он направлен на восприятие кода человеком, а не на достижение абсолютно формализуемых критериев. Так что, если захотеть, можно довести до абсурда абсолютно любой закон и принцип, потому что даже фундаментальные понятия — coupling, cohesion, readability — не формализуемы, а все остальное в основном от них и пляшет.

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

ARK>А что, перенос вызова в другой метод нашего объекта уменьшает число связей?

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

ARK> Если мы используем напрямую Order.Customer (неважно, в каком методе), то по-прежнему, если кто-то изменит реализацию Customer, нам придется переделывать свой код. Нет?


Нет. Потому что если я что то поменяю в Customer, будет лучше если перед глазами не будет маячить код, в котором, помимо Customer, еще и Customer.Address.Country какой нибудь присутствует.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
AVK Blog
Re[11]: Закон Деметры
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.07.13 09:54
Оценка:
Здравствуйте, samius, Вы писали:

S>В данном — да, не очень важно. Но вообще в контексте LoD — очень важно. Ведь нам надо точно знать, у каких объектов (даже экземпляров) какие методы можно звать.

Имхо, формулировка закона плоха.

S>Отлично. Обратите внимание, что ваши типы зависимостей не зависят от способа получения объекта, как в LoD.

Это согласуется с интуитивным пониманием целей закона.

S>Прежде мы вынуждены разобраться с тем, чем зависимость от метода foo.Bar() хуже зависимости от метода FooBar(foo). И чем потенциальные повреждения в одном случае хуже потенциальных повреждений в другом.

Вопрос не в зависимости от foo.Bar(), а о зависимости от того, что вернёт foo.Bar().

S>Из формулировки закона можно сделать весьма сомнительное следствие, говорящее о том что вызов через точку — зло.

В доисторические времена закон звучал примерно так: "вызов через две точки — зло".
А современная формулировка — попытки прижать хитрецов, которые заменяют foo.bar().baz() на t = foo.bar(); t.baz().

S>>Примерно так.

S>Все как бы верно. Только причиной создания метода GetCustomerGreetingLine стал не LoD, а уточнение требований (или оверинженеринг).
Не совсем. LoD потребовал бы от нас засунуть в order метод GetCustomerFirstName(), потому что через две точки — нельзя.
А greeting — это уже результат анализа всех мест использования GetcustomeFirstName(), проведённого при code review.

Вчера было достаточно GetFirstName-а без учета представителей и доверенностей. И опять таки, по тому же LoD метод order.GetCustomerGreetingLine() дает меньшую гибкость чем Order::GetCustomerGreetingLine(order).
Не вижу, где. Вы имеете в виду, что у результата Order::GetCustomerGreetingLine(order) я буду иметь право вызывать методы? Не понимаю.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.07.13 09:55
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


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

нельзя сказать однозначно без знания о том, имеют ли пункты формулировки приоритеты.

S>>Но все-таки закон не о том, какие объекты писать, а о том, какие методы не вызывать.


ARK>Не вызывать методы "внутреннего" объекта.

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

S>>Да, Боб добавляет неразбирихи. Оказывается, что надо делать различие между объектами и структурами данных. Знать лишнее об объектах плохо, а о структурах данных — нет.


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

вот-вот.
Re[12]: Закон Деметры
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.07.13 10:05
Оценка: 1 (1)
Здравствуйте, Sinclair, Вы писали:

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


S>>В данном — да, не очень важно. Но вообще в контексте LoD — очень важно. Ведь нам надо точно знать, у каких объектов (даже экземпляров) какие методы можно звать.

S>Имхо, формулировка закона плоха.
Согласен.

S>>Отлично. Обратите внимание, что ваши типы зависимостей не зависят от способа получения объекта, как в LoD.

S>Это согласуется с интуитивным пониманием целей закона.
Получается что интуитивное понимание целей закона расходится с формальным описанием способа его применения.

S>>Прежде мы вынуждены разобраться с тем, чем зависимость от метода foo.Bar() хуже зависимости от метода FooBar(foo). И чем потенциальные повреждения в одном случае хуже потенциальных повреждений в другом.

S>Вопрос не в зависимости от foo.Bar(), а о зависимости от того, что вернёт foo.Bar().
FooBar(foo) вернет то же самое и имеет точно такие же риски изменений, как и foo.Bar().

S>>Из формулировки закона можно сделать весьма сомнительное следствие, говорящее о том что вызов через точку — зло.

S>В доисторические времена закон звучал примерно так: "вызов через две точки — зло".
S>А современная формулировка — попытки прижать хитрецов, которые заменяют foo.bar().baz() на t = foo.bar(); t.baz().
Да, помню такие времена. Однако, на хитрецов, которые решат не делать у объекта экземплярных методов, у закона ничего нет. И не было.

S>>>Примерно так.

S>>Все как бы верно. Только причиной создания метода GetCustomerGreetingLine стал не LoD, а уточнение требований (или оверинженеринг).
S>Не совсем. LoD потребовал бы от нас засунуть в order метод GetCustomerFirstName(), потому что через две точки — нельзя.
Нет, он этого буквально не требует. Он ведь разрешает так же метод Order.GetCustomerFirstName(order).
S>А greeting — это уже результат анализа всех мест использования GetcustomeFirstName(), проведённого при code review.
Вот и я об этом. Но раз мы предположили наличие результата такого анализа, точно так же можем предположить и отсутствие такого результата, либо анализа.

S>Вчера было достаточно GetFirstName-а без учета представителей и доверенностей. И опять таки, по тому же LoD метод order.GetCustomerGreetingLine() дает меньшую гибкость чем Order::GetCustomerGreetingLine(order).

S>Не вижу, где. Вы имеете в виду, что у результата Order::GetCustomerGreetingLine(order) я буду иметь право вызывать методы? Не понимаю.
Нет, я имею в виду что Order::GetCustomerGreetingLine(order) можно будет вызывать откуда угодно, не оглядываясь на LoD. А oder.GetSustomerGreetingLine() лишь в тех местах, где order получен легально по LoD.
Re[6]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 10:26
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Пока что я вижу что тебя интересует доведение до абсурда, имхо. В дизайне нет и не может быть законов, которые формализуются до уровня математических теорем, потому что почти все они это эмпирика, а не выведенные математически правила. А эмпирика потому что он направлен на восприятие кода человеком, а не на достижение абсолютно формализуемых критериев. Так что, если захотеть, можно довести до абсурда абсолютно любой закон и принцип, потому что даже фундаментальные понятия — coupling, cohesion, readability — не формализуемы, а все остальное в основном от них и пляшет.


Я понимаю вашу позицию. Тем не менее, не исключаю, что формализация некоторых — сейчас неформализованных, но формализуемых в принципе — вещей вполне может привести к общему улучшению этих самых "неформализуемых в принципе" фундаментальных понятий. Собственно, тема как раз об этом.

ARK>> Если мы используем напрямую Order.Customer (неважно, в каком методе), то по-прежнему, если кто-то изменит реализацию Customer, нам придется переделывать свой код. Нет?

AVK>Нет. Потому что если я что то поменяю в Customer, будет лучше если перед глазами не будет маячить код, в котором, помимо Customer, еще и Customer.Address.Country какой нибудь присутствует.

Вот это не понял. Я понимаю так:

  // клиентский код
  var country = order.Customer.Address.Country;   // BAD. знаем слишком много об объекте Order
  DoSomething(country);

  ...

  order.DoSomethingWithCountry();   // OK. ничего не знаем, можно менять Order как угодно

  ...

  this.DoSomething(order);      // а тут чего хорошего? опять надо будет переделывать _наш_ класс, если изменится Customer
Re[8]: Закон Деметры
От: Кодт Россия  
Дата: 24.07.13 14:16
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Скажите лучше, что вы думаете о законе Деметры.

ARK>После чтения ваших постов у меня сложилось впечатление, что вы интересуетесь всякими теоретическими изысканиями.

Ненавижу закон Деметры! Ё)

Существует несколько крайностей в практике создания крупных объектов.
Одна ось крайностей — степень открытости-закрытости. Слева — кишки наружу, справа — всё закрыто интерфейсами и водопроводными методами.
Другая — алгебраичность-неповторимость. Слева — композиции композиций безымянных структур, справа — каждый тип сделан по-своему, так что любые полиморфные операции над ними нужно специализировать, т.е. вводить прослойку адаптеров.

Самый ураган в том, что алгебраичность прекрасно уживается с закрытостью. Кортеж интерфейсов к объектам, управляющим кортежами интерфейсов.

Прямо сейчас ковыряюсь в OpenFST — так там алгебраичность плюс закрытость, это адъ для отладки.

Но больше всего достаёт то, что контексты для вычисления передаются не (только) как параметры методов, но и как члены-данные объектов. Вот где зло. И пофиг, делается ли это через кишки наружу или через водопроводные методы.
Водопровод позволяет расставить прерывания и отследить, кто что где меняет. А кишки — добавить интересующие поля в watch window.
Хороший, абстрагирующий водопровод — мешает добавить вотчи. Алгебраический шаблонный водопровод — мешает расставлять прерывания. Алгебраические кишки — снимают всякую уверенность, что кто-то где-то не имеет внезапный доступ к данным.

Извините, наболело. Не могу быть беспристрастен. Поэтому см. первую строку ответа.
Перекуём баги на фичи!
Re[9]: Закон Деметры
От: AlexRK  
Дата: 24.07.13 14:44
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Ненавижу закон Деметры! Ё)


Понятно.

К>Самый ураган в том, что алгебраичность прекрасно уживается с закрытостью. Кортеж интерфейсов к объектам, управляющим кортежами интерфейсов.


Не можете привести простенький пример? А то я что-то медитировал-медитировал, да не вымедитировал. Речь о функциях, возвращающих вместо примитивных типов указатели на сложные структуры?

К>Но больше всего достаёт то, что контексты для вычисления передаются не (только) как параметры методов, но и как члены-данные объектов. Вот где зло.


Дык — в точном соответствии с принципами, изложенными в Clean Code.

К>Алгебраический шаблонный водопровод — мешает расставлять прерывания. Алгебраические кишки — снимают всякую уверенность, что кто-то где-то не имеет внезапный доступ к данным.


Вот это тоже не понял. А кто у нас поимеет доступ к данным мимо водопроводных методов? Или "алгебраические кишки" выдаются наружу через указатель с возможностью модификации?
Re[10]: Закон Деметры
От: Кодт Россия  
Дата: 25.07.13 23:45
Оценка:
Здравствуйте, AlexRK, Вы писали:

К>>Самый ураган в том, что алгебраичность прекрасно уживается с закрытостью. Кортеж интерфейсов к объектам, управляющим кортежами интерфейсов.


ARK>Не можете привести простенький пример? А то я что-то медитировал-медитировал, да не вымедитировал. Речь о функциях, возвращающих вместо примитивных типов указатели на сложные структуры?


Речь о мега-пимплах такого вида
template<class Filter1, class Filter2>
class ComplexFilter : FilterBase< ComplexFilterImpl<Filter1, Filter2> > // PIMPL плюс CRTP
{
public:
  virtual void init() { base_->init(); }
  virtual void setstate(FilterState fs) { base_->setstate(fs); }
  virtual FilterState filter(Arc* a, FilterState* fs) { base_->filter(a1,a2,fs); }
};

template<class F> void setstate(F* f, FilterState fs) { f->F::setstate(fs); } // форсируем статический вызов вместо виртуального
template<class F> FilterState filter(F* f, Arc* a, FilterState fs) { f->F::filter(a,fs); } // форсируем статический вызов вместо виртуального

template<class Filter1, class Filter2>
class ComplexFilterImpl
{
  Filter1* f1_;
  Filter2* f2_;
public:
  void setstate(FilterState fs)
  {
    ::setstate(f1_, fs.GetState1());
    ::setstate(f2_, fs.GetState2());
  }
  FilterState filter(Arc* a, FilterState fs)
  {
    return WhatFilterToUse()->filter(a, use_first() ? fs.GetState1() : fs.GetState2() );
  }
  IFilter* WhatFilterToUse()
  {
    return use_first() ? f1_ : f2_;
  }
};

Я издеваюсь и утрирую, но при этом очень сильно преуменьшаю.
OpenFST изобилует такими схемами.

К>>Но больше всего достаёт то, что контексты для вычисления передаются не (только) как параметры методов, но и как члены-данные объектов. Вот где зло.


ARK>Дык — в точном соответствии с принципами, изложенными в Clean Code.


Дык и что в этом хорошего, если мутабельность на мутабельности мутабельностью погоняет.
Если любой скрытый параметр может поменяться в недрах любого метода.
Это не God Object, а Devil Staff.

К>>Алгебраический шаблонный водопровод — мешает расставлять прерывания. Алгебраические кишки — снимают всякую уверенность, что кто-то где-то не имеет внезапный доступ к данным.


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


Алгебраические кишки делаются так, что те, кому нужен прямой доступ, — просто наследуются от разнообразных хелперов и встраиваются в цепочку алгебраически сконструированных шаблонов. А кому не нужен, те используют готовые базовые хелперы.
С самого верхнего уровня доступа к потрохам нет... если предварительно не встроился.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.