Mempertimbangkan perangkat lunak menengah-besar dengan arsitektur n-layer dan injeksi ketergantungan, saya merasa nyaman untuk mengatakan bahwa objek yang dimiliki lapisan dapat bergantung pada objek dari lapisan bawah tetapi tidak pernah pada objek dari lapisan yang lebih tinggi.
Tapi saya tidak yakin apa yang harus dipikirkan tentang objek yang bergantung pada objek lain dari lapisan yang sama.
Sebagai contoh, mari kita asumsikan aplikasi dengan tiga lapisan dan beberapa objek seperti yang ada di gambar. Jelas dependensi top-down (panah hijau) ok, bottom-up (panah merah) tidak ok, tetapi bagaimana dengan dependensi di dalam lapisan yang sama (panah kuning)?
Tidak termasuk ketergantungan sirkular, saya ingin tahu tentang masalah lain yang dapat timbul dan seberapa banyak arsitektur berlapis dilanggar dalam kasus ini.
sumber
Jawaban:
Ya, objek dalam satu lapisan dapat memiliki dependensi langsung satu sama lain, kadang-kadang bahkan yang siklik - itulah yang sebenarnya membuat perbedaan inti dengan dependensi yang diperbolehkan antara objek di lapisan yang berbeda, di mana tidak ada dependensi langsung yang diizinkan, atau hanya dependensi yang ketat arah.
Namun, itu tidak berarti mereka harus memiliki ketergantungan seperti itu secara sewenang-wenang. Sebenarnya tergantung pada apa yang diwakili lapisan Anda, seberapa besar sistem itu dan dan apa tanggung jawab bagian-bagian itu. Perhatikan bahwa "arsitektur berlapis" adalah istilah yang tidak jelas, ada variasi besar dari apa yang sebenarnya berarti dalam berbagai jenis sistem.
Misalnya, katakanlah Anda memiliki "sistem berlapis horizontal", dengan lapisan basis data, lapisan bisnis, dan lapisan antarmuka pengguna (UI). Katakanlah layer UI mengandung setidaknya beberapa lusinan kelas dialog yang berbeda.
Seseorang dapat memilih desain di mana tidak ada kelas dialog yang bergantung pada kelas dialog lain secara langsung. Seseorang dapat memilih desain di mana "dialog utama" dan "sub dialog" ada dan hanya ada dependensi langsung dari dialog "utama" ke "sub". Atau seseorang dapat memilih desain di mana setiap kelas UI yang ada dapat menggunakan / menggunakan kembali kelas UI lain dari lapisan yang sama.
Ini semua adalah pilihan desain yang mungkin, mungkin lebih atau kurang masuk akal tergantung pada jenis sistem yang Anda bangun, tetapi tidak satu pun dari mereka membuat "pelapisan" sistem Anda tidak valid.
sumber
Sejujurnya, saya tidak berpikir Anda harus nyaman dengan itu. Ketika berurusan dengan apa pun kecuali sistem yang sepele, saya bertujuan untuk memastikan semua lapisan hanya bergantung pada abstraksi dari lapisan lain; keduanya lebih rendah dan lebih tinggi.
Jadi misalnya,
Obj 1
tidak harus bergantungObj 3
. Itu harus memiliki ketergantungan pada misalnyaIObj 3
dan harus diberitahu implementasi yang abstraksi itu untuk bekerja dengan saat runtime. Hal yang melakukan penceritaan seharusnya tidak terkait dengan level mana pun karena tugasnya adalah memetakan dependensi tersebut. Itu mungkin wadah IoC, kode khusus yang disebut misalnya olehmain
yang menggunakan DI murni. Atau dengan dorongan itu bahkan bisa menjadi pencari layanan. Apapun, dependensi tidak ada di antara lapisan sampai hal itu menyediakan pemetaan.Saya berpendapat bahwa ini adalah satu-satunya waktu Anda harus memiliki dependensi langsung. Ini bagian dari cara kerja lapisan dalam itu dan dapat diubah tanpa mempengaruhi lapisan lainnya. Jadi itu bukan kopling berbahaya.
sumber
Mari kita lihat ini secara praktis
Obj 3
sekarang tahuObj 4
ada. Terus? Kenapa kita peduli?OK tapi, bukankah semua objek abstraksi?
OK tapi, jika objek saya dienkapsulasi dengan benar bukankah itu menyembunyikan detail?
Beberapa orang suka membabi buta bersikeras bahwa setiap objek membutuhkan antarmuka kata kunci. Saya bukan salah satu dari mereka. Saya suka secara membabi buta bersikeras bahwa jika Anda tidak akan menggunakannya sekarang Anda perlu rencana untuk berurusan dengan membutuhkan sesuatu seperti mereka nanti.
Jika kode Anda sepenuhnya dapat di-refactor pada setiap rilis, Anda bisa mengekstrak antarmuka nanti jika Anda membutuhkannya. Jika Anda telah menerbitkan kode yang tidak ingin Anda kompilasi ulang dan mendapati diri Anda berharap Anda berbicara melalui antarmuka, Anda memerlukan rencana.
Obj 3
tahuObj 4
ada. Tapi apakahObj 3
tahu kalauObj 4
beton?Ini di sini adalah mengapa sangat bagus untuk TIDAK menyebar ke
new
mana-mana. JikaObj 3
tidak tahu apakahObj 4
konkret, kemungkinan karena itu tidak membuatnya, maka jika Anda menyelinap masuk dan berubahObj 4
menjadi kelas abstrakObj 3
tidak akan peduli.Jika Anda bisa melakukan itu, maka
Obj 4
abstrak sepenuhnya selama ini. Satu-satunya hal yang membuat antarmuka di antara mereka dari awal membuat Anda adalah jaminan bahwa seseorang tidak akan sengaja menambahkan kode yang memberikan yangObj 4
konkret saat ini. Konstruktor yang dilindungi dapat mengurangi risiko itu tetapi itu mengarah ke pertanyaan lain:Apakah Obj 3 dan Obj 4 dalam paket yang sama?
Objek sering dikelompokkan dalam beberapa cara (paket, namespace, dll). Ketika dikelompokkan dengan bijak, ubahlah dampak yang lebih mungkin terjadi dalam suatu kelompok daripada lintas kelompok.
Saya suka mengelompokkan berdasarkan fitur. Jika
Obj 3
danObj 4
berada di grup dan lapisan yang sama, sangat tidak mungkin Anda telah menerbitkan satu dan tidak ingin membuatnya kembali sementara hanya perlu mengubah yang lain. Itu berarti benda-benda ini cenderung mendapat manfaat dari memiliki abstraksi yang diletakkan di antara mereka sebelum memiliki kebutuhan yang jelas.Jika Anda melewati batas grup, meskipun itu ide yang bagus untuk membiarkan objek di kedua sisi berbeda secara independen.
Seharusnya sesederhana itu, tetapi sayangnya Java dan C # telah membuat pilihan yang tidak menguntungkan yang menyulitkan ini.
Dalam C # itu tradisi untuk memberi nama setiap antarmuka kata kunci dengan
I
awalan. Itu memaksa klien untuk TAHU mereka berbicara dengan antarmuka kata kunci. Itu mengacaukan rencana refactoring.Di Jawa tradisi menggunakan pola penamaan yang lebih baik:
FooImple implements Foo
Namun, ini hanya membantu pada tingkat kode sumber karena Java mengkompilasi antarmuka kata kunci ke biner yang berbeda. Itu berarti ketika Anda refactorFoo
dari klien konkret ke abstrak yang tidak memerlukan satu karakter kode yang diubah masih harus dikompilasi ulang.BUGS dalam bahasa-bahasa khusus inilah yang membuat orang tidak bisa menunda abstraksi formal sampai mereka benar-benar membutuhkannya. Anda tidak mengatakan bahasa apa yang Anda gunakan tetapi mengerti ada beberapa bahasa yang tidak memiliki masalah ini.
Anda tidak mengatakan bahasa apa yang Anda gunakan jadi saya hanya akan mendorong Anda untuk menganalisis bahasa dan situasi Anda dengan cermat sebelum Anda memutuskan itu akan menjadi antarmuka kata kunci di mana-mana.
Prinsip YAGNI memainkan peran kunci di sini. Tetapi begitu juga "Tolong buat sulit untuk menembak diri sendiri di kaki".
sumber
Selain jawaban di atas, saya pikir itu mungkin membantu Anda untuk melihatnya dari sudut pandang yang berbeda.
Misalnya, dari sudut pandang Ketergantungan . DR adalah aturan yang disarankan oleh Robert C. Martin untuk Arsitektur Bersihnya yang terkenal .
Ia mengatakan
Dengan kebijakan tingkat yang lebih tinggi , yang ia maksud adalah abstraksi tingkat yang lebih tinggi . Komponen yang bocor pada detail implementasi, seperti misalnya antarmuka atau kelas abstrak berbeda dengan kelas konkret atau struktur data.
Masalahnya, aturannya tidak terbatas pada ketergantungan antar-lapisan. Itu hanya menunjukkan ketergantungan antara potongan-potongan kode, terlepas dari lokasi atau lapisan milik mereka.
Jadi tidak, tidak ada yang salah secara inheren dengan memiliki ketergantungan antara elemen-elemen dari lapisan yang sama. Namun, ketergantungan masih dapat diterapkan untuk menyampaikan dengan prinsip ketergantungan yang stabil .
Sudut pandang lain adalah SRP.
Decoupling adalah cara kami untuk memutus dependensi berbahaya dan menyampaikan dengan beberapa praktik terbaik seperti dependensi inversi (IoC). Namun, elemen-elemen yang memiliki alasan untuk berubah mereka tidak memberikan alasan untuk decoupling karena elemen-elemen dengan alasan yang sama untuk berubah akan berubah pada saat yang sama (sangat mungkin) dan mereka akan digunakan pada waktu yang sama juga. Jika itu adalah kasus antara
Obj3
danObj4
kemudian, sekali lagi, tidak ada yang salah secara inheren.sumber