Apakah operator tanpa syarat C # 6.0 yang baru bertentangan dengan Hukum Demeter?


29

The Law of Demeter menyatakan berikut ini:

  • Setiap unit harus memiliki pengetahuan yang terbatas tentang unit lain: hanya unit "erat" yang terkait dengan unit saat ini.
  • Setiap unit hanya boleh berbicara dengan teman-temannya; jangan berbicara dengan orang asing.
  • Hanya berbicara dengan teman dekat Anda.

C # 6.0 memperkenalkan operator baru yang disebut operator null-kondisional . IMHO, itu membuat pengkodean lebih mudah dan meningkatkan keterbacaan. Tetapi juga membuatnya lebih mudah untuk menulis lebih banyak kode yang digabungkan, karena lebih mudah untuk menavigasi melalui bidang kelas, sudah memeriksa nullity (sesuatu seperti var x = A?.B?.C?.D?.E?.F?).

Apakah benar menyatakan bahwa operator baru ini bertentangan dengan Hukum Demeter?


2
Mengapa Anda yakin itu A?.B?.C?.D?.E?.F?akan melanggarnya - LoD bukan tentang berapa banyak titik dan jika metode panggilan memiliki informasi tentang struktur yang tidak melanggar poinnya, panggilan semacam itu akan dapat diterima dengan sempurna. Bahwa kode tersebut bisa melanggar LOD tidak cukup untuk mengatakan bahwa semua penggunaan itu melakukan melanggar LOD.

14
Membaca " Hukum Demeter Bukanlah Latihan Menghitung Titik "? Ini membahas contoh yang tepat ini.
outis

@outis: excelent baca. Saya tidak mengatakan bahwa setiap kode dalam bentuk X.Y.Z.W.Umerupakan pelanggaran terhadap "hukum". Tapi, dalam pengalaman saya berurusan dengan kode, 90% dari waktu itu hanya kode digabungkan jelek.
Arthur Rizzo

2
@ ArthurRizzo tapi itu bukan masalah dengan operator kondisional nol yang melawan LoD. Itu adalah kode yang salah. Operator hanyalah alat untuk mempermudah manusia membacanya. Tidak .?ada lagi pelanggaran LoD daripada +atau -tidak.

1
RC Martin membedakan antara kelas data murni dan kelas perilaku. Jika Properti yang diakses mengekspos data internal dari kelas perilaku snippet pasti melanggar LoD, tetapi ini tidak ada hubungannya dengan operator kondisi nol. Lagi pula, properti tidak terikat untuk mengekspos data internal, yang mungkin bau, tetapi tidak melanggar LoD. Menurut RC Martin skema tersebut mungkin benar-benar valid dengan kelas data murni.
Paul Kertscher

Jawaban:


44

Apakah benar menyatakan bahwa operator baru ini bertentangan dengan Hukum Demeter?

Tidak *


* Operator bersyarat nol adalah alat dalam bahasa dan kerangka kerja NET. Setiap alat memiliki kemampuan untuk disalahgunakan dan digunakan dengan cara yang dapat membahayakan maintainability aplikasi tertentu.

Tapi fakta bahwa alat dapat menjadi disalahgunakan tidak berarti bahwa itu memiliki untuk disalahgunakan, atau bahwa alat ini melanggar prinsip tertentu (s) yang mungkin diadakan.

Hukum Demeter dan lainnya adalah pedoman tentang bagaimana Anda harus menulis kode Anda. Itu ditargetkan untuk manusia, bukan alat. Jadi fakta bahwa bahasa C # 6.0 memiliki alat baru di dalamnya tidak mempengaruhi bagaimana Anda seharusnya menulis dan menyusun kode Anda.

Dengan alat baru, Anda perlu mengevaluasi sebagai ... jika orang yang berakhir menjaga kode Anda akan menjadi psikopat ... . Perhatikan lagi, bahwa ini adalah panduan untuk orang yang menulis kode dan bukan tentang alat yang digunakan.


foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1);akan baik-baik saja (dan tidak lebih buruk dari foo[0][0][0][0][0] = 1) ... dan banyak situasi lain di mana seperti itu tidak melanggar LoD.

@MichaelT Ketika Anda mulai masuk ke matriks dari dimensi itu, sepertinya akan lebih mudah untuk memperlakukan indeks sebagai vektor / tuple / array itu sendiri dan membiarkan internal kelas matriks khawatir tentang bagaimana data sebenarnya disimpan. (Yang, sekarang saya pikirkan, adalah penerapan Hukum Demeter, setidaknya terkait dengan enkapsulasi.)
JAB

(Dan, tentu saja, praktik semacam itu membuatnya lebih mudah untuk mengimplementasikan pengirisan multidimensi dan memiliki beberapa alat matriks yang sangat kuat.)
JAB

1
@ JAB Saya baru saja mencoba memberikan sebuah contoh. Kemungkinan yang lebih baik Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ...adalah masalah pemrosesan struktur kode bodoh. Ini bukan orang asing ... hanya bodoh. Itu .?menjadi sangat berguna dalam struktur seperti itu.

10

Semacam.

Jika Anda hanya melakukan satu akses ( a?.Foo) maka itu setara dengan:

a == null ? null : a.Foo

yang disetujui kebanyakan orang bukanlah pelanggaran terhadap Hukum Demeter. Pada titik itu, itu hanya gula sintaksis untuk meningkatkan keterbacaan.

Lebih dari itu, dan itu mungkin akan melanggar Hukum Demeter, dan fitur ini cenderung mempromosikan penggunaan semacam itu. Saya bahkan akan mengatakan bahwa penggunaan "baik" di atas saja tidak cukup untuk menjamin perubahan semacam ini ke bahasa, jadi saya berharap bahwa itu dibuat untuk mendukung penggunaan yang kurang jelas baik.

Yang mengatakan, perlu diingat bahwa Hukum Demeter bukan hukum semata, tetapi lebih sebagai pedoman. Banyak kode yang melanggarnya dan berfungsi dengan baik. Kadang-kadang kesederhanaan desain atau kode bernilai lebih dari risiko yang ditimbulkan dengan melanggar Hukum Demeter.


sesuatu yang lebih dari itu tidak selalu merusak LoD misalnya pola builder
jk.

@ Telastyn: Sintaks bahasa baru yang kita bicarakan tidak mendukung metode panggilan: a?.Func1(x)?.Func2(y) Operator penggabungan nol adalah sesuatu yang lain.
Ben Voigt

@ BenVoigt - ah, saya akan keluar dari artikel, yang menunjukkan itu hanya bekerja dengan bidang, properti, dan pengindeks. Saya tidak punya MSVS2015 berguna untuk diuji. Kamu benar.
Telastyn

1
a? .Foo tidak cukup setara dengan == null? null: a.Foo. Yang pertama mengevaluasi hanya sekali, yang terakhir mengevaluasi dua kali. Itu bisa jadi masalah jika a adalah iterator.
Loren Pechtel

9

Tidak. Mari kita pertimbangkan operatornya sendiri, dan penggunaan berantai yang Anda miliki untuk itu.

Sendiri .?Atergantung pada jumlah pengetahuan yang sama dari kelas nilai-kiri dan jenis yang dikembalikan oleh metode seperti .A != nullhalnya, yaitu. Perlu diketahui bahwa Aproperti itu ada dan mengembalikan nilai yang dapat dibandingkan null.

Kita hanya bisa berargumen bahwa ini melanggar hukum Demeter jika properti yang diketik memang demikian. Kami bahkan tidak dipaksa untuk memiliki Ajenis beton (nilainya dapat berupa jenis turunan). Kopling di sini minimal.

Sekarang mari kita pertimbangkan var x = A?.B?.C?.D?.E?.F.

Yang berarti bahwa Aharus dari jenis yang bisa nol, atau bisa memiliki Bproperti, yang harus dari jenis yang bisa nol atau memiliki Cproperti, dan seterusnya sampai jenis Eproperti menjadi sesuatu yang bisa nol atau bisa memiliki Fproperti.

Dengan kata lain, kita perlu melakukan ini dengan bahasa yang diketik secara statis atau menerapkan batasan pada jenis yang dapat dikembalikan jika pengetikan longgar. C # dalam kebanyakan kasus menggunakan pengetikan statis, jadi kami tidak mengubah apa pun.

Jika kami punya maka kode berikut juga akan melanggar hukum:

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;
    }
  }
}

Yang persis sama . Kode ini yang menggunakan kopling elemen yang berbeda perlu "tahu" tentang rantai penuh kopling, tetapi menggunakan kode yang tidak melanggar Hukum Demeter untuk melakukannya, dengan setiap unit memiliki kopling yang terdefinisi dengan baik dengan selanjutnya.


3
+1 operator baru hanyalah gula sintaksis untuk resep pahit yang telah Anda jelaskan.
Ross Patterson

1
Nah, jika seorang dev menulis kode yang sepertinya saya pikir lebih mudah untuk memperhatikan bahwa ada sesuatu yang tidak beres. Saya tahu bahwa operatornya adalah 100% gula sintaksis, tetapi tetap saja, saya pikir orang-orang cenderung lebih nyaman menulis sesuatu seperti var x = A?.B?.C?.D?.E?.Fdaripada semua jika / elses meskipun pada akhirnya mereka sama.
Arthur Rizzo

2
Lebih mudah untuk melihat ada sesuatu yang tidak beres A?.B?.C?.D?.E?.Fkarena ada sedikit yang bisa salah; entah kita harus mencoba Fmelalui jalur itu, atau kita tidak seharusnya, sementara bentuk yang lebih panjang bisa memiliki kesalahan di dalamnya serta kesalahan itu tidak menjadi hal yang benar untuk dilakukan.
Jon Hanna

@ArthurRizzo Tetapi jika Anda mengaitkan jenis kode di atas dengan pelanggaran LoD, maka mudah untuk melewatkannya dalam kasus di mana tidak ada pemeriksaan nol yang diperlukan dan Anda bisa melakukannya A.B.C.D. Jauh lebih sederhana untuk memiliki satu hal yang harus diwaspadai (akses properti dirantai) daripada dua hal berbeda yang bergantung pada detail yang sangat tidak relevan (pengecekan nol)
Ben Aaronson

5

Objek dapat dibuat untuk tujuan merangkum perilaku atau menyimpan data, dan objek dapat dibuat untuk tujuan dibagikan dengan kode luar atau dipegang secara pribadi oleh penciptanya.

Objek yang dibuat untuk tujuan perilaku enkapsulasi (apakah dibagikan atau tidak), atau untuk yang dibagikan dengan kode luar (apakah mereka merangkum perilaku atau data) umumnya harus diakses melalui antarmuka permukaan mereka. Namun, ketika benda-benda yang menyimpan data dibuat untuk digunakan secara eksklusif oleh penciptanya, alasan Hukum-Demeter normal untuk menghindari akses "dalam" tidak berlaku. Jika bagian dari kelas yang menyimpan atau memanipulasi data dalam objek diubah dengan cara yang memerlukan penyesuaian kode lain, akan mungkin untuk menjamin bahwa semua kode tersebut akan diperbarui karena - seperti disebutkan di atas - objek dibuat untuk penggunaan eksklusif satu kelas.

Sementara aku memikirkannya? Operator mungkin bisa dirancang lebih baik, ada situasi yang cukup di mana objek menggunakan struktur data bersarang sehingga operator memiliki banyak kasus penggunaan yang tidak akan melanggar prinsip-prinsip yang dinyatakan oleh Hukum Demeter. Fakta bahwa itu dapat digunakan untuk melanggar LoD tidak boleh dianggap sebagai argumen terhadap operator, karena itu tidak lebih buruk daripada "." operator dalam hal itu.


Mengapa Hukum Demeter tidak berlaku untuk benda yang memegang data?
Telastyn

2
@ Telastyn: Tujuan dari LoD adalah untuk menghindari masalah yang mungkin timbul jika sepotong kode mengakses objek batin yang mungkin dimanipulasi atau dipedulikan oleh sesuatu yang lain . Jika tidak ada hal lain di alam semesta yang dapat memanipulasi atau peduli dengan keadaan objek batin, maka tidak perlu untuk menjaga terhadap masalah seperti itu.
supercat

Saya tidak yakin saya setuju. Bukan karena hal-hal lain mungkin peduli memodifikasi data, itu adalah bahwa Anda menyambung ke objek yang terkandung melalui jalur (pada dasarnya tiga titik penggandengan - dua objek dan hubungannya). Terkadang kopling itu tidak menjadi masalah besar, tetapi masih terasa bau bagi saya.
Telastyn

@ Telastyn: Maksud Anda tentang jalan adalah bagus, tapi saya pikir maksud saya baik-baik saja. Mengakses suatu objek melalui banyak jalur menciptakan sambungan di antara jalur tersebut. Jika beberapa akses melalui jalur dangkal, maka akses melalui jalur dalam juga dapat menyebabkan kopling yang tidak diinginkan. Namun, jika semua akses melalui satu jalur mendalam tertentu, tidak akan ada apa pun untuk jalur mendalam itu menuju pasangan.
supercat

@ Telastyn Tidak apa-apa untuk melintasi struktur data untuk mendapatkan data jauh di dalam. Ini tidak sama dengan pemanggilan metode bersarang. Anda kadang-kadang diminta untuk mengetahui suatu struktur data dan bagaimana cara bersarang, hal yang sama tidak berlaku untuk objek seperti layanan dan layanan / repositori bersarangnya sendiri, dll.
Per Hornshøj-Schierbeck
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.