Чи відповідає новий нульовий умовний оператор C # 6.0 проти Закону Деметра?


29

Закон Деметри говорить наступне:

  • Кожен підрозділ повинен мати лише обмежені знання про інші підрозділи: лише одиниці, "тісно" пов'язані з поточним підрозділом.
  • Кожен підрозділ повинен спілкуватися лише зі своїми друзями; не розмовляйте з незнайомцями.
  • Поговоріть лише зі своїми найближчими друзями.

C # 6.0 представив нового оператора, який називається оператором з нульовими умовами . IMHO, це полегшує кодування та покращує читабельність. Але це також полегшує написання більш зв'язаного коду, так як легше пересуватися по полях класу, вже перевіряючи наявність недійсності (щось подібне 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. На думку RC Martin, схема може бути абсолютно дійсною для чистих класів даних.
Пол Кертчер

Відповіді:


44

Чи правильно стверджувати, що цей новий оператор суперечить Закону Деметера?

Ні *


* Нульовий умовний оператор - це інструмент у мові та .NET рамках. Будь-який інструмент має можливість зловживати ним та використовувати його способами, які можуть завдати шкоди ремонтопридатності даної програми.

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

Закон Деметра та інші - це вказівки щодо того, як слід писати свій код. Він орієнтований на людей, а не на інструменти. Тож факт, що мова 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

з якою більшість людей погодиться, це не є порушенням Закону про Деметер. На даний момент це просто синтаксичний цукор для поліпшення читабельності.

Більше того, і це, ймовірно, порушило б Закон Деметера, і ця особливість, як правило, сприяє такому використанню. Я б навіть сказав, що вищезгаданого "хорошого" використання лише недостатньо для того, щоб гарантувати подібні зміни мови, тому я сподіваюся, що це було зроблено для підтримки менш очевидного використання.

З огляду на це, варто пам’ятати, що Закон Деметера - це не закон як такий, а більше керівний принцип. Багато коду порушує його і працює добре. Іноді простота дизайну чи коду коштує більше, ніж ризик, який створює порушення Закону про деметер.


нічого більше, ніж це, не обов'язково порушує LoD, наприклад, зразок builder
jk.

@Telastyn: Новий синтаксис мови, про який ми говоримо, робить виклики методу підтримки: a?.Func1(x)?.Func2(y) Оператор з’єднання нуля - це щось інше.
Бен Войгт

@BenVoigt - ах, я вийшов із статті, в якій вказано, що вона працює лише з полями, властивостями та індексаторами. У мене не було MSVS2015 під рукою для тестування. Ти правий.
Теластин

1
a? .oo не зовсім рівнозначно a == null? null: a.Foo. Перший оцінює лише один раз, останній оцінює його двічі. Це могло б мати значення, якщо б був ітератор.
Лорен Печтел

9

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

Сам по собі .?Aзалежить від того самого обсягу знань класу, яке має ліве значення, і типу, що повертається методом, як .A != nullі, саме. Потрібно знати про те, що Aвластивість існує і повертає значення, яке можна порівняти null.

Ми можемо лише стверджувати, що це порушує закон Деметера, якщо це роблять введені властивості. Нас навіть не змушують Aсприймати як конкретний тип (його значення може бути похідного типу). Муфта тут мінімальна.

Тепер давайте розглянемо var x = A?.B?.C?.D?.E?.F.

Що означає, що він Aповинен бути типу, який може бути нульовим, або може мати Bвластивість, який повинен бути типу, який може бути нульовим або мати Cвластивість, і так далі, поки тип Eвластивості не стане чимось, що може бути недійсним або може мати 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всіх тих, хто / ін, навіть якщо вони зрештою однакові.
Артур Ріццо

2
Легше помітити, що щось не так, A?.B?.C?.D?.E?.Fтому що може бути не так; або ми повинні намагатися пройти Fцей шлях, або не повинні, хоча довша форма може мати помилки всередині нього, а також помилка, що це не правильно.
Джон Ханна

@ArthurRizzo Якщо ви пов'язуєте вищезгаданий вид коду з порушеннями LoD, то їх легко пропустити у випадку, коли не потрібна перевірка нуля, і ви просто можете це зробити A.B.C.D. Набагато простіше мати одну річ, на яку слід звернути увагу (прикутий доступ до власності), а не дві різні речі, які залежать від досить нерелевантних деталей (перевірка на нуль)
Бен Ааронсон

5

Об'єкт може бути створений з метою інкапсуляції поведінки або зберігання даних, а об'єкти можуть створюватися з метою спільного використання із зовнішнім кодом або утримуватися приватно їх творцем.

До об'єктів, які створюються з метою інкапсуляції поведінки (будь то спільна чи ні), або для спільного використання із зовнішнім кодом (незалежно від того, чи вони вони інкапсулюють поведінку чи дані), як правило, слід отримувати доступ через їх поверхневий інтерфейс. Коли об’єкти, що зберігають дані, створені для використання виключно їхнім автором, однак, звичайні причини Законодавства про уникнення "глибокого" доступу не застосовуються. Якщо частина класу, яка зберігає або маніпулює даними в об'єкті, буде змінена способом, який потребував би коригування іншого коду, можна буде гарантувати, що весь такий код оновлюється, оскільки - як зазначено вище - об'єкт був створений для виключне використання одного класу.

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


Чому Закон про деметер не поширюється на об’єкти, що зберігають дані?
Теластин

2
@Telastyn: Мета LoD - уникнути проблем, які можуть виникнути, якщо фрагмент коду має доступ до внутрішніх об'єктів, якими щось інше може маніпулювати чи турбувати . Якщо ніщо інше у Всесвіті не могло б маніпулювати або піклуватися про стан внутрішніх предметів, то не потрібно захищати від таких проблем.
supercat

Я не впевнений, що згоден. Справа не в тому, що інші речі можуть піклуватися про зміну даних, це те, що ви з'єднуються з об'єктом, що міститься, шляхом доріжки (по суті, три точки з’єднання - два об'єкти та їх відношення). Іноді ця зв'язок не буде великою справою, але все одно мені здається смердючою.
Теластин

@Telastyn: Ваша думка про шляхи хороша, але я думаю, що моя думка справедлива. Доступ до об'єкта через декілька шляхів створює зв'язок між цими шляхами. Якщо деякий доступ здійснюється через неглибокий шлях, то також доступ через глибокий шлях може спричинити небажане з'єднання. Якщо весь доступ здійснюється через один конкретний глибокий шлях, то для цього глибокого шляху до пари не буде нічого.
supercat

@Telastyn Цілком чудово пройти структуру даних, щоб дістатися до даних глибоко вниз. Це не те саме, що виклики вкладеного методу. Вам іноді потрібно знати структуру даних і те, як вона вкладена, те ж саме не стосується таких об’єктів, як служба та власні вкладені служби / сховища тощо.
Per Hornshøj-Schierbeck
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.