Новый нуль-условный оператор C # 6.0 противоречит Закону Деметры?

29

Закон Деметры гласит следующее:

  • Каждый юнит должен иметь только ограниченные знания о других юнитах: только юниты, «тесно» относящиеся к текущему юниту.
  • Каждый юнит должен разговаривать только со своими друзьями; не разговаривай с незнакомцами
  • Поговорите только со своими непосредственными друзьями.

В C # 6.0 введен новый оператор, называемый нулевым условным оператором . ИМХО, это облегчает кодирование и улучшает читабельность. Но это также облегчает написание более связанного кода, так как легче перемещаться по полям класса, уже проверяя на ничтожность (что-то вроде var x = A?.B?.C?.D?.E?.F?).

Правильно ли утверждать, что этот новый оператор противоречит Закону Деметры?

Артур Риццо
источник
2
Почему вы полагаете, что A?.B?.C?.D?.E?.F?это нарушит его - LoD не о том, сколько точек, и если вызывающий метод имеет такую ​​информацию о структуре, которая не нарушает его точек, такой вызов будет вполне приемлемым. Что такое код может нарушить LoD не достаточно , чтобы сказать , что все виды использования него делают нарушают Lod.
14
Чтение " Закон Деметры - не упражнение по подсчету точек "? Здесь обсуждается именно этот пример.
outis
@outis: отличное чтение. Я не говорю, что каждый код в форме X.Y.Z.W.Uявляется нарушением «закона». Но, по моему опыту работы с кодом, 90% времени это просто уродливый код.
Артур Риццо
2
@ArthurRizzo, но это не проблема с нулевым условным оператором, идущим против LoD. Это код, который виноват. Оператор - это всего лишь инструмент, упрощающий чтение человеком. Не .?больше не нарушает LoD чем +или -делает.
1
RC Martin различает классы чистых данных и поведенческие классы. Если доступ к свойствам предоставляет внутренние данные поведенческого класса, фрагмент, безусловно, нарушает LoD, но это не имеет ничего общего с нулевым условным оператором. В любом случае, свойства не обязаны раскрывать внутренние данные, которые могут быть запахом, но не нарушают LoD. По мнению Р.К. Мартина, схема может быть абсолютно действительной с классами чистых данных.
Пол Керчер

Ответы:

44

Правильно ли утверждать, что этот новый оператор противоречит Закону Деметры?

Нет *


* Нулевой условный оператор - это инструмент в языке и .NET Framework. Любой инструмент имеет возможность злоупотребления и использования таким образом, который может нанести ущерб ремонтопригодности данного приложения.

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

Закон Деметры и другие являются руководством о том, как вы должны написать свой код. Он нацелен на людей, а не на инструменты. Поэтому тот факт, что язык C # 6.0 имеет новый инструмент, не обязательно влияет на то, как вы должны писать и структурировать свой код.

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

Сообщество
источник
foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);было бы хорошо (и не хуже, чем foo[0][0][0][0][0] = 1) ... и множество других ситуаций, где такое не нарушает LoD.
@MichaelT Когда вы начинаете разбираться в матрицах этой размерности, кажется, что стало бы легче обращаться с индексами как с самим вектором / кортежем / массивом, и позволить внутренним элементам класса матрицы беспокоиться о том, как на самом деле хранятся данные. (Что, теперь, когда я думаю об этом, является применением Закона Деметры, по крайней мере, в том, что касается инкапсуляции.)
JAB
(И, конечно, такая практика облегчает реализацию многомерного среза и имеет некоторые действительно мощные матричные инструменты.)
JAB
1
@JAB Я просто пытался придумать пример. Лучше, вероятно, было бы Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...- это проблема обработки структуры какого-то глупого кода. Это не незнакомец ... это просто глупо. Это .?становится очень полезным в таких структурах.
10

Вроде.

Если вы делаете только один доступ ( a?.Foo), то это эквивалентно:

a == null ? null : a.Foo

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

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

Тем не менее, стоит помнить, что Закон Деметры не является законом как таковым, а скорее ориентиром. Много кода нарушает его и работает хорошо. Иногда простота дизайна или кода стоит больше, чем риск, связанный с нарушением закона Деметры.

Telastyn
источник
что-либо большее, чем это, не обязательно нарушает LoD, например, шаблон компоновщика
jk.
@Telastyn: новый синтаксис языка, о котором мы говорим , поддерживает вызовы методов: a?.Func1(x)?.Func2(y) оператор объединения нулей - это нечто другое.
Бен Фойгт
@BenVoigt - ах, я уходил из статьи, в которой говорилось, что она работает только с полями, свойствами и индексаторами. У меня не было MSVS2015 под рукой для тестирования. Вы правы.
Теластин
1
a. .Foo не совсем эквивалентно == null? null: a.Foo. Первый оценивает только один раз, второй оценивает его дважды. Это может иметь значение, если бы итератор.
Лорен Печтел
9

Нет. Давайте рассмотрим как самого оператора, так и интенсивное использование для него.

Само по себе .?Aзависит от того же объема знаний о классе, который имеет левое значение, и о типе, возвращаемом методом .A != null, а именно: viz. Необходимо знать, что Aсвойство существует и возвращает значение, с которым можно сравнить null.

Мы можем только утверждать, что это нарушает закон Деметры, если типизированные свойства делают. Мы даже не обязаны иметь Aконкретный тип (его значение может быть производного типа). Связь здесь минимальная.

Теперь давайте рассмотрим var x = A?.B?.C?.D?.E?.F.

Это означает, что он Aдолжен иметь тип, который может иметь значение NULL или иметь Bсвойство, которое должно иметь тип, который может иметь значение NULL или иметь Cсвойство, и так далее, пока тип Eсвойства не будет иметь значение, которое может быть NULL или может иметь Fсобственность.

Другими словами, мы должны делать это либо со статически типизированным языком, либо наложить ограничения на типы, которые могут быть возвращены, если типизация свободна. C # в большинстве случаев использует статическую типизацию, поэтому мы ничего не изменили.

Если бы мы имели тогда, следующий кодекс также нарушил бы закон:

ExplicitType x;
var b = A.B;
if (b == null)
  x = null;
else
{
  var c = b.C;
  if (c == null)
    x = null;
  else
  {
    var d = c.D;
    if (d == null)
      x = null;
    else
    {
      var e = d.E;
      if (e == null)
        x = null;
      else
        x = e.F;
    }
  }
}

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

Джон Ханна
источник
3
+1 новый оператор - просто синтаксический сахар для описанного вами горького рецепта.
Росс Паттерсон
1
Что ж, если разработчик пишет код, который выглядит так, я думаю, легче заметить, что что-то может быть не так. Я знаю, что оператор на 100% является синтетическим сахаром, но, тем не менее, я думаю, что людям, как правило, удобнее писать что-то наподобие var x = A?.B?.C?.D?.E?.Fвсех этих if / elses, даже если в конце они одинаковы.
Артур Риццо
2
Проще заметить, что что-то не A?.B?.C?.D?.E?.Fтак, потому что меньше может быть неправильным; либо мы должны пытаться пройти Fпо этому пути, либо мы не должны этого делать , в то время как в более длинной форме могут быть ошибки, а также ошибка, которая не является правильной.
Джон Ханна
@ArthurRizzo Но если вы связываете вышеупомянутый вид кода с нарушениями LoD, то легко пропустить их в случае, когда нет необходимости в нулевой проверке, и вы можете просто сделать A.B.C.D. Гораздо проще иметь одну вещь, на которую нужно обращать внимание (доступ к цепочечному свойству), а не две разные вещи, которые зависят от довольно нерелевантных деталей (проверка на ноль)
Бен Ааронсон,
5

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

К объектам, которые создаются с целью инкапсулирования поведения (совместно используемого или нет) или для совместного использования с внешним кодом (независимо от того, инкапсулируют ли они поведение или данные), обычно следует получать доступ через их поверхностный интерфейс. Однако, когда объекты, содержащие данные, создаются для использования исключительно их создателем, обычные причины закона-де-метра для избежания «глубокого» доступа не применяются. Если часть класса, которая хранит или манипулирует данными в объекте, изменяется таким образом, что потребуется корректировка другого кода, можно будет гарантировать, что весь такой код будет обновлен, потому что - как отмечено выше - объект был создан для эксклюзивное использование одного класса.

Пока думаю что? Оператор, возможно, мог бы быть лучше спроектирован, существует достаточно ситуаций, когда объекты используют вложенные структуры данных, и у оператора есть много вариантов использования, которые не будут нарушать принципы, выраженные Законом Деметры. Тот факт, что он может быть использован для нарушения LoD, не должен рассматриваться как аргумент против оператора, поскольку он не хуже, чем "." оператор в этом отношении.

Supercat
источник
Почему Закон Деметры не применяется к объектам хранения данных?
Теластин
2
@Telastyn: цель LoD состоит в том, чтобы избежать проблем, которые могут возникнуть, если часть кода получает доступ к внутренним объектам, которыми может манипулировать или беспокоиться что- то еще . Если ничто иное во вселенной не может манипулировать или заботиться о состоянии внутренних объектов, тогда нет необходимости принимать меры против таких проблем.
суперкат
Я не уверен, что согласен. Дело не в том, что другие вещи могут заботиться об изменении данных, а в том, что вы соединяетесь с содержащимся объектом через путь (по сути, три точки соединения - два объекта и их отношение). Иногда такая связь не будет иметь большого значения, но все же кажется мне вонючей.
Теластин
@Telastyn: Ваша точка зрения о путях хорошая, но я думаю, что моя точка зрения в порядке. Доступ к объекту через несколько путей создает связь между этими путями. Если какой-то доступ осуществляется по неглубокому пути, то доступ по глубокому пути также может вызвать нежелательное соединение. Однако, если весь доступ осуществляется через один конкретный глубокий путь, для этого глубокого пути не будет ничего, к чему можно было бы присоединиться.
суперкат
@Telastyn Совершенно нормально пройти через структуру данных, чтобы получить данные в глубине. Это не то же самое, что вызовы вложенных методов. Иногда требуется знать структуру данных и то, как она вложена, то же самое не относится к объектам, таким как служба, и к собственным вложенным службам / репозиториям и т. Д.
Пер Хорншой-Ширбек