Закон Demeter и Class Constructors

Можно использовать LINQ для объектов на наборе строк, как так:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
5
задан RTBarnard 6 January 2016 в 21:42
поделиться

3 ответа

Поскольку Закон Деметры гласит, что вы не должны проектировать внешний интерфейс объекта так, чтобы он выглядел так, как будто он состоит из некоторых других объектов с известными интерфейсами, которые клиенты могут просто захватить of и получить доступ.

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

Например, если вы передаете параметр URL в конструктор вашего объекта HTTPRequest, то он не t означает, что HTTPRequest должен иметь метод getURL, который возвращает объект URL, для которого вызывающий должен затем вызвать getProtocol, getQueryString и т. д. Если кто-то, у кого есть объект HTTPRequest, может захотеть узнать протокол запроса, он должен (Закон говорит) узнать, вызвав getProtocol для того объекта, который у них есть, а не для какого-либо другого объекта, который, как им известно, HTTPRequest хранится внутри.

Идея состоит в том, чтобы уменьшить взаимосвязь - без Закона Деметры пользователь должен знать интерфейс к HTTPRequest и URL для получения протокола. С законом им нужен только интерфейс для HTTPRequest. И HTTPRequest.getProtocol () явно может возвращать «http» без необходимости участия какого-либо объекта URL в обсуждении.

Тот факт, что иногда пользователь объекта запроса оказывается тем, кто его создал, и поэтому также использует интерфейс URL для передачи параметра, не является ни здесь, ни там. Не все пользователи объектов HTTPRequest сами создали их. Таким образом, клиенты, которые в соответствии с законом имеют право на доступ к URL-адресу, потому что они создали его сами, могут делать это таким образом, а не отбирать его обратно из запроса. Клиенты, которые не создали URL, не могут.

Лично я считаю, что Закон Деметры, обычно изложенный в простой форме, взломан. Они серьезно говорят, что если у моего объекта есть строковое поле Name, и я хочу знать, содержит ли Name какие-либо символы, отличные от ASCII, то я должен либо определить метод NameContainsNonASCIICharacters для моего объекта, а не смотреть на саму строку, или же добавить функцию visitName к классу, принимающему функцию обратного вызова, чтобы обойти ограничение, убедившись, что строка является параметром функции, которую я написал? Это никак не меняет связи, а просто заменяет методы получения на методы посетителей. Должен ли каждый класс, возвращающий целое число, иметь полный набор арифметических операций на случай, если я хочу манипулировать возвращаемым значением? getPriceMultipliedBy (int n)? Конечно, нет.

Что это полезно, так это то, что когда вы его нарушаете, вы можете спросить себя, почему вы его нарушаете, и можно ли разработать лучший интерфейс, не нарушая его. Часто можно, но на самом деле это зависит от того, о каких объектах вы говорите. Определенные интерфейсы могут быть безопасно связаны с обширными фрагментами кода - такими как целые числа, строки и даже URL,

13
ответ дан 18 December 2019 в 07:31
поделиться

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

var a = new A();
var x = a.B.doSomething();

Вместо этого вы делаете это ...

var a = new A();
var x = a.doSomething(); // where a.doSomething might call b.doSomething();

У этого есть свои преимущества, поскольку для вызывающих абонентов все становится проще (Car.Start () по сравнению с Car.Engine.Start ()) , но у вас есть много маленьких методов упаковки. Вы также можете использовать шаблон посредника , чтобы смягчить этот тип «нарушения».

4
ответ дан 18 December 2019 в 07:31
поделиться

Ответ JP довольно хорош, так что это просто дополнение, а не несогласие или другая замена.

Как я понимаю эту эвристику, вызов A не должен прерываться, потому что изменения класса B. Поэтому, если вы связываете вызовы с помощью abfoo (), тогда интерфейс A становится зависимым от интерфейса B, нарушая правило. Вместо этого вы должны вызвать a.BFoo (), который вызывает для вас b.foo ().

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

Я бы также добавил, что, строго говоря, это правило постоянно нарушается для определенной группы вездесущих классов, таких как строка. Возможно, приемлемо решить, какие классы также повсеместны на определенном уровне приложения, и полностью игнорировать «Правило» Деметры для них.

4
ответ дан 18 December 2019 в 07:31
поделиться