У меня три вопроса относительно закона Деметры.
Помимо классов, которые были специально назначены для возврата объектов - таких как классы фабрики и компоновщика - нормально ли, чтобы метод возвращал объект, например, объект, удерживаемый одним из свойств класса, или будет нарушать закон Деметры (1) ? И если он нарушает закон Деметры, имеет ли значение, если возвращаемый объект является неизменным объектом, представляющим фрагмент данных и содержащий только геттеры для этих данных (2а)? Или такой ValueObject сам по себе является антишаблоном, потому что все, что делается с данными в этом классе, выполняется вне класса (2b)?
В псевдокоде:
class A {}
class B {
private A a;
public A getA() {
return this.a;
}
}
class C {
private B b;
private X x;
public void main() {
// Is it okay for B.getA to exist?
A a = this.b.getA();
a.doSomething();
x.doSomethingElse(a);
}
}
Я подозреваю, что закон Деметера запрещает такую модель, как приведенная выше. Что я могу сделать, чтобы это doSomethingElse()
можно было назвать, не нарушая закон (3)?
источник
x
настроиться?x
- это просто другое свойство, значением которого является просто некоторый объект, который может вызыватьсяdoSomethingElse
.user.currentSession.isLoggedIn()
. потому что он связывает клиентовuser
не только соuser
своим сотрудником, но и с нимsession
. Вместо этого вы хотите иметь возможность писатьuser.isLoggedIn()
. обычно это достигается добавлениемisLoggedIn
методаuser
, делегату реализации которогоcurrentSession
.Ответы:
Во многих языках программирования все возвращаемые значения являются объектами. Как уже говорили другие, неспособность использовать методы возвращаемых объектов заставляет вас вообще ничего не возвращать. Вы должны спросить себя: «Каковы обязанности класса А, В и С?» Вот почему использование метасинтаксических имен переменных, таких как A, B и C, всегда запрашивает ответ «это зависит», потому что в этих терминах нет обязанностей наследования.
Возможно, вы захотите взглянуть на Domain Driven Design, который даст вам более детальный набор эвристик, чтобы рассуждать о том, куда должна идти функциональность и кто должен что вызывать.
Ваш второй вопрос, касающийся неизменных объектов, касается понятия объекта-
значения по сравнению с объектом-сущностью. в DDD Вы можете передавать объекты-значения практически без ограничений. Это не говорит правду для объектов сущности.
Упрощенный LOD намного лучше выражен в DDD как правила доступа к агрегатам . Никакие внешние объекты не должны содержать ссылки на элементы агрегатов. Должна быть только корневая ссылка.
Помимо DDD, вы, по крайней мере, хотите разработать собственные наборы стереотипов для своих классов, подходящие для вашей области. Применяйте правила в отношении этих стереотипов при разработке вашей системы.
Также всегда помните, что все эти правила должны управлять сложностью, а не ограничивать себя.
источник
Law of Demeter
,tell, don't ask
,getters are evil
,object encapsulation
и ,single responsibility principle
кажется, сводятся к этому вопросу: когда следует делегации на объект домена используется в отличие от получения значения объекта и делать что - то с этим? Было бы очень полезно иметь возможность применять нюансы квалификации в ситуациях, когда такое решение должно быть принято.Да, это, безусловно, так и есть.
Давайте посмотрим на основные моменты :
Все три из них оставляют у вас один вопрос: кто друг?
Принимая решение о том, что возвращать, Закон Деметры или принцип наименьшего знания (LoD) не требует от вас защиты от кодировщиков, которые настаивают на его нарушении. Это диктует, что вы не заставляете кодеров нарушать его.
Смущение - вот почему многие считают, что сеттер всегда должен возвращать пустоту. Нет. Вы должны разрешить способ создания запросов (получателей), которые не изменяют состояние системы. Это базовое разделение командных запросов .
Означает ли это, что вы свободны вникать в кодовую базу, объединяющую все, что вы хотите? Нет. Соединяйте только то, что должно было быть вместе. В противном случае цепь может измениться, и вдруг ваши вещи сломаются. Это то, что подразумевается под друзьями.
Длинные цепи могут быть предназначены для. Свободные интерфейсы, iDSL, большая часть Java8 и старый добрый StringBuilder предназначены для создания длинных цепочек. Они не нарушают LoD, потому что все в цепочке предназначено для совместной работы и обещают продолжать работать вместе. Вы нарушаете деметру, когда цепляете вместе вещи, которые никогда не слышали друг о друге. Друзья - это те, кто обещал, что ваша сеть будет работать. Друзья друзей не сделали.
Это только создает возможность нарушить деметру. Это не нарушение. Это даже не обязательно плохо.
Неизменное это хорошо, но не имеет значения здесь. Пройдя сквозь длинную цепочку, не становится лучше. То, что делает это лучше, отделяет получение от использования. Если вы используете, спросите, что вам нужно в качестве параметра. Не иди на охоту, углубляясь в добытчиков незнакомцев.
Прежде чем говорить о том,
x.doSomethingElse(a)
понимаю, что вы в основном написалиb.getA().doSomething()
Теперь LoD - это не упражнение по подсчету точек . Но когда вы создаете цепочку, вы говорите, что знаете, как получить
A
(используяB
), и знаете, как использоватьA
. НуA
иB
лучше быть близкими друзьями , потому что вы только в сочетании их вместе.Если вы только что попросил что - нибудь рукой для Вас ,
A
как вещь , которую вы могли бы использоватьA
и не волновало , откуда она иB
могла прожить жизнь счастливой и свободной от вашей одержимости получатьA
отB
.Что касается
x.doSomethingElse(a)
отсутствия подробностей о том, откудаx
взялось, LoD вообще нечего сказать по этому поводу.LoD может способствовать отделению использования от строительства . Но я укажу, если вы неукоснительно относитесь к каждому объекту как к дружественному, вы застрянете при написании кода в статических методах. Таким образом, вы можете построить удивительно сложный граф объектов в основном таким образом, но в конечном итоге вам придется вызвать метод, чтобы начать работу. Вы просто должны решить, кто ваши друзья. От этого никуда не деться.
Так что да, классу разрешено возвращать одного из его членов в LoD. Когда вы это сделаете, вы должны уточнить, дружит ли этот член с вашим классом, потому что, если это не так, какой-то клиент может попытаться соединить вас с этим классом, используя вас, чтобы получить его, прежде чем использовать его. Это важно, потому что теперь то, что вы возвращаете, должно всегда поддерживать это использование.
Есть много случаев, когда это просто не проблема. Коллекции могут игнорировать это просто потому, что они предназначены дружить со всем, что их использует. Точно так же объекты ценности дружат со всеми. Но если вы написали утилиту проверки адреса, которая требовала от объекта сотрудника, из которого он извлек адрес, вместо того, чтобы просто запросить адрес, то лучше надеяться, что и сотрудник, и адрес получены из одной и той же библиотеки.
источник
settings.darkTheme().monospaceFont()
просто синтаксический сахар дляsettings.darkTheme(); settings.monospaceFont();
Я не совсем понимаю этот аргумент. Любой объект может вернуть объект в результате вызова сообщения. Особенно, если вы думаете «все как объект».
Классы, однако, являются единственными объектами, которые могут создавать новые объекты своего вида, отправляя им «новое» сообщение (или часто заменяемое новым ключевым словом в большинстве распространенных языков ООП)
В любом случае, в отношении вашего вопроса я бы с подозрением отнесся к объекту, возвращающему одно из своих свойств для использования в другом месте. Может случиться так, что у этого объекта будет утечка информации о его состоянии или деталях реализации.
И вы не хотели бы, чтобы объект C знал о деталях реализации B. Это нежелательная зависимость от объекта A с точки зрения объекта C.
Поэтому вы можете спросить себя, не знает ли C, зная A, слишком много для той работы, которую он должен выполнять, независимо от LOD.
Есть случаи, когда это может быть законным, например, уместно запрашивать объект коллекции для одного из его элементов и не нарушать никакого правила Деметры. С другой стороны, запрашивать объект Book для объекта SQLConnection рискованно и его следует избегать.
LOD существует потому, что многие программисты, как правило, запрашивают информацию у объектов перед выполнением работы. LOD напоминает нам, что иногда (если не всегда) мы просто слишком усложняем вещи, желая, чтобы наши объекты делали слишком много. Иногда нам просто нужно попросить другой объект сделать работу за нас. LOD помогает нам дважды подумать о том, где мы размещаем поведение в наших объектах.
источник
Почему бы не быть? Похоже, вы предполагаете, что необходимо сообщить своим коллегам-программистам, что класс специально предназначен для возврата объектов или он вообще не должен возвращать объекты. На языках, где все является объектом, это сильно ограничит ваши возможности, так как вы вообще не сможете ничего вернуть.
источник
b.getA().doSomething()
иx.doSomethingElse(b.getA())
A a = b.getA(); a.DoSomething();
- вернуться к одной точке.b.getA().doSomething()
иx.doSomethingElse(b.getA())
вы должны были явно спросить об этом. Похоже, ваш вопрос, похоже, оthis.b.getA()
.A
не членC
, а не создано вC
и не предоставляетсяC
, кажется , что использованиеA
вC
(в любой форме) нарушает Lod. Подсчет точек - это не то, чем занимается LoD, это всего лишь иллюстративный инструмент. Можно преобразоватьDriver().getCar().getSteeringWheel().getFrontWheels().toRight()
в отдельные операторы, каждый из которых содержит одну точку, но нарушение LoD все еще существует. Я читал во многих сообщениях на этом форуме , что выполнение действия должны быть переданы на объект , который реализует фактическое поведение тtell don't ask
. Возвращаемые значения, кажется, конфликтуют с этим.Зависит от того, что вы делаете. Если вы выставляете члена своего класса, когда никто не занимается тем, что он является членом вашего класса, или каков тип этого члена, это плохо. Если вы затем измените тип этого члена и тип функции, возвращающей член, и каждый должен изменить свой код, то это зло.
С другой стороны, если интерфейс вашего класса заявляет, что он будет предоставлять экземпляр хорошо известного класса A, это нормально. Если вам повезло, и вы можете сделать это, предоставив ученика своего класса, хорошо для вас. Если вы изменили этот элемент с A на B, вам нужно будет переписать метод, который возвращает A, очевидно, теперь он должен будет сконструировать его и заполнить доступными данными, а не однострочными, возвращающими член. Интерфейс вашего класса будет неизменным.
источник