Apakah praktik yang buruk untuk melewatkan instance melalui beberapa lapisan?

60

Dalam desain program saya, saya sering sampai pada titik di mana saya harus melewati instance objek melalui beberapa kelas. Misalnya, jika saya memiliki pengontrol yang memuat file audio, dan kemudian meneruskannya ke pemain, dan pemain meneruskannya ke playerRunnable, yang meneruskannya lagi di tempat lain dll. Terlihat agak buruk, tapi saya tidak tahu bagaimana menghindarinya. Atau apakah boleh melakukan ini?

EDIT: Mungkin contoh pemain bukan yang terbaik karena saya bisa memuat file nanti, tetapi dalam kasus lain itu tidak berfungsi.

Keping
sumber

Jawaban:

54

Seperti yang disebutkan orang lain, ini tidak selalu merupakan praktik yang buruk, tetapi Anda harus memperhatikan bahwa Anda tidak melanggar pemisahan lapisan dari kekhawatiran dan melewati contoh spesifik lapisan di antara lapisan. Misalnya:

  • Objek database tidak boleh dilewatkan ke lapisan yang lebih tinggi. Saya telah melihat program menggunakan .NET's DataAdapter class, DB-access class, dan meneruskannya ke lapisan UI, daripada menggunakan DataAdapter di DAL, membuat DTO atau dataset, dan meneruskannya. Akses DB adalah domain DAL.
  • Objek UI tentu saja harus dibatasi pada lapisan UI. Sekali lagi, saya telah melihat ini dilanggar, baik dengan ListBox diisi dengan data pengguna diteruskan ke lapisan BL, bukannya array / DTO isinya, dan (favorit saya), kelas DAL yang mengambil data hierarkis dari DB, dan bukannya mengembalikan struktur data hierarkis, itu hanya membuat dan mengisi objek TreeView, dan meneruskannya kembali ke UI untuk ditambahkan secara dinamis ke formulir.

Namun, jika instance yang Anda lewati adalah DTO atau entitas itu sendiri, itu mungkin ok.

Avner Shahar-Kashtan
sumber
1
Ini mungkin terdengar mengejutkan, tetapi di hari-hari awal yang gelap. NET yang merupakan praktik yang umum direkomendasikan dan mungkin lebih baik daripada apa yang dilakukan kebanyakan tumpukan lainnya.
Wyatt Barnett
1
Saya tidak setuju. Memang benar bahwa Microsoft mendukung praktik aplikasi lapis tunggal di mana klien Winforms juga mengakses DB, dan DataAdapter ditambahkan langsung ke formulir sebagai kontrol yang tak terlihat, tapi itu hanya arsitektur tertentu, berbeda dari pengaturan N-tier OP . Tetapi dalam arsitektur multilayer, dan ini berlaku untuk VB6 / DNA bahkan sebelum. NET, objek DB tetap berada di lapisan DB.
Avner Shahar-Kashtan
Untuk memperjelas: Anda pernah melihat orang mengakses UI secara langsung (dalam contoh Anda, kotak daftar) dari "Lapisan Data" mereka? Saya tidak berpikir saya menemukan pelanggaran seperti itu dalam kode produksi .. wow ..
Simon Whitehead
7
@SimonWhitehead Tepat. Orang-orang yang tidak jelas tentang perbedaan antara ListBox dan array, dan menggunakan ListBox sebagai DTO. Itu adalah momen yang membantu saya menyadari betapa banyak asumsi tak terlihat yang saya buat yang tidak intuitif untuk orang lain.
Avner Shahar-Kashtan
1
@SimonWhitehead - Ya, tentu saja saya telah melihat bahwa dalam program VB6 dan VB.NET Framework 1.1 dan 2.0 dan telah ditugaskan untuk memelihara monster tersebut. Itu menjadi sangat jelek, sangat cepat.
jfrankcarr
15

Menarik bahwa belum ada yang membicarakan objek yang tidak dapat berubah . Saya berpendapat bahwa melewatkan objek abadi di sekitar semua lapisan sebenarnya adalah hal yang baik daripada membuat banyak objek berumur pendek untuk setiap lapisan.

Ada beberapa diskusi hebat tentang ketidakberdayaan oleh Eric Lippert di blog-nya

Di sisi lain saya berpendapat bahwa melewatkan objek yang bisa berubah di antara lapisan adalah desain yang buruk . Pada dasarnya Anda membuat layer dengan janji bahwa layer di sekitarnya tidak akan mengubahnya dengan cara memecahkan kode Anda.

M Afifi
sumber
13

Melewatkan contoh objek di sekitar adalah hal biasa untuk dilakukan. Ini mengurangi kebutuhan untuk menjaga keadaan (misalnya variabel instan) dan memisahkan kode dari konteks eksekusi.

Salah satu masalah yang mungkin Anda hadapi adalah refactoring, ketika Anda harus mengubah tanda tangan beberapa metode di sepanjang rantai doa sebagai tanggapan terhadap perubahan persyaratan parameter metode di dekat bagian bawah rantai itu. Namun, ini dapat dikurangi dengan penggunaan alat pengembangan perangkat lunak modern yang membantu dalam refactoring.

dasblinkenlight
sumber
6
Saya akan mengatakan masalah lain yang mungkin Anda hadapi melibatkan imutabilitas. Saya dapat mengingat bug yang sangat membingungkan dalam proyek yang saya kerjakan di mana pengembang memodifikasi DTO tanpa memikirkan fakta bahwa kelas khususnya bukan satu - satunya yang merujuk pada objek itu.
Phil
8

Mungkin kecil, tetapi ada risiko menempatkan referensi ini di suatu tempat di salah satu lapisan yang berpotensi menyebabkan referensi yang menggantung atau kebocoran memori di kemudian hari ..

techfoobar
sumber
Poin Anda benar, tetapi dari terminologi OP ("instance instance objek"), saya merasa bahwa ia memberikan nilai (bukan pointer) atau ia berbicara tentang lingkungan yang dikumpulkan sampah (Java, C #, Python, Go,. ..)
Mohammad Dehghan
7

Jika Anda melewati objek hanya karena dibutuhkan di daerah terpencil kode Anda, menggunakan inversi kontrol dan pola desain injeksi dependensi bersama dengan opsional wadah IoC yang tepat dapat dengan baik memecahkan masalah tentang membawa contoh objek sekitar. Saya telah menggunakannya pada proyek berukuran sedang dan tidak akan pernah lagi mempertimbangkan untuk menulis sepotong besar kode server tanpa menggunakannya.

Steven Schlansker
sumber
Kedengarannya menarik, saya sudah menggunakan injeksi konstruktor, dan saya pikir komponen tingkat tinggi saya mengontrol komponen tingkat rendah. Bagaimana Anda menggunakan wadah IOC untuk menghindari kemacetan?
Puckl
Saya rasa saya menemukan jawabannya di sini: martinfowler.com/articles/injection.html
Puckl
1
Catatan tambahan, jika Anda bekerja di Java, Guice benar-benar bagus, dan Anda dapat mengatur ikatan ke hal-hal seperti permintaan sehingga komponen tingkat tinggi menciptakan ruang lingkup dan mengikat instans ke kelas yang benar pada waktu itu.
Dave
4

Melewati data melalui banyak lapisan bukanlah hal yang buruk, ini satu-satunya cara sistem layered dapat bekerja tanpa melanggar struktur layered. Tanda ada masalah adalah ketika Anda mengirimkan data Anda ke beberapa objek di lapisan yang sama untuk mencapai tujuan Anda.

Ryathal
sumber
3

Jawaban Cepat: Tidak ada yang salah dalam melewati instance objek. Seperti yang juga disebutkan, intinya adalah melompati pemberian referensi ini di semua lapisan yang berpotensi menyebabkan referensi yang menggantung atau kebocoran memori.

Dalam proyek kami, kami menggunakan praktik ini untuk lulus DTO (objek transfer data) antara lapisan dan itu adalah praktik yang sangat membantu. Kami juga menggunakan kembali objek dto kami untuk membangun lebih kompleks sekali, seperti untuk informasi ringkasan.

EL Yusubov
sumber
3

Saya terutama seorang web UI dev tetapi bagi saya sepertinya ketidaknyamanan intuitif Anda mungkin kurang tentang contoh pass-through dan lebih banyak tentang fakta bahwa Anda akan sedikit prosedural dengan controller itu. Haruskah controller Anda berkeringat semua detail ini? Mengapa bahkan referensi lebih dari satu nama objek lain untuk mendapatkan audio diputar?

Dalam desain OOP, saya cenderung berpikir tentang apa yang selalu hijau dan apa yang lebih mungkin berubah. Subjek untuk mengubah hal-hal adalah apa yang Anda ingin cenderung untuk dimasukkan ke dalam kotak objek yang lebih besar sehingga Anda dapat mempertahankan antarmuka yang konsisten bahkan ketika pemain berubah atau opsi baru ditambahkan. Atau Anda menemukan diri Anda ingin menukar objek audio atau komponen di dalamnya secara grosir.

Dalam hal ini, pengontrol Anda perlu mengidentifikasi bahwa ada kebutuhan untuk memutar file audio dan kemudian memiliki cara yang konsisten / selalu hijau untuk membuatnya diputar. Hal-hal pemutar audio di sisi lain dapat dengan mudah berubah ketika teknologi dan platform diubah atau pilihan baru ditambahkan. Semua perincian itu harus berada di bawah antarmuka objek komposit yang lebih besar, IMO, dan Anda tidak perlu menulis ulang pengontrol Anda saat perincian tentang bagaimana audio dimainkan berubah. Kemudian ketika Anda melewatkan contoh objek dengan detail seperti lokasi file ke objek yang lebih besar semua yang bertukar dilakukan di interior konteks yang sesuai di mana seseorang cenderung melakukan sesuatu yang konyol dengannya.

Jadi dalam hal ini saya tidak berpikir bahwa objek objek yang dilempar yang mungkin mengganggu Anda. Kapten Picard berlari ke ruang mesin untuk menghidupkan inti warp, berlari kembali ke jembatan untuk merencanakan koordinat, dan kemudian menekan tombol "punch-it" setelah menyalakan perisai alih-alih hanya mengatakan "Ambil kita ke planet X di Warp 9. Jadikan begitu. " dan membiarkan krunya memilah detailnya. Karena ketika dia menanganinya seperti itu, dia bisa menjadi kapten kapal apa pun di armada tanpa mengetahui tata letak setiap kapal dan bagaimana semuanya bekerja. Dan itulah akhirnya kemenangan desain OOP terbesar untuk IMO.

Erik Reppen
sumber
2

Ini adalah desain yang cukup umum yang akhirnya terjadi walaupun Anda mungkin memiliki masalah latensi jika aplikasi Anda sensitif terhadap hal semacam itu.

James
sumber
2

Masalah ini dapat diselesaikan dengan variabel cakupan dinamis, jika bahasa Anda memilikinya, atau penyimpanan thread-lokal. Mekanisme ini memungkinkan kami mengaitkan beberapa variabel khusus dengan rantai aktivasi atau untaian kontrol, sehingga kami tidak harus meneruskan nilai-nilai ini ke dalam kode yang tidak ada hubungannya dengan mereka hanya agar dapat dikomunikasikan ke beberapa kode lain yang memang membutuhkannya.

Kaz
sumber
2

Seperti yang ditunjukkan oleh jawaban lain, ini bukan desain yang buruk. Itu dapat membuat kopling ketat antara kelas bersarang dan yang bersarang, tetapi melonggarkan kopling mungkin bukan opsi yang valid jika bersarang referensi memberikan nilai pada desain.

Salah satu solusi yang mungkin adalah "meratakan" referensi bersarang di kelas controller.

Alih-alih melewatkan parameter beberapa kali melalui objek bersarang, Anda bisa mempertahankan referensi kelas pengontrol ke semua objek bersarang.

Bagaimana tepatnya ini diterapkan (atau jika itu bahkan solusi yang valid) tergantung pada desain sistem saat ini, seperti:

  • Apakah Anda dapat mempertahankan semacam peta objek bersarang di controller tanpa terlalu rumit?
  • Ketika Anda meneruskan parameter ke objek bersarang yang sesuai, dapatkah objek bersarang mengenali parameter dengan segera, atau adakah fungsionalitas tambahan yang terjadi saat meneruskannya melalui objek bersarang?
  • dll.

Ini adalah masalah yang saya temui dalam pola desain MVC untuk klien GXT. Komponen GUI kami berisi komponen GUI bersarang untuk beberapa lapisan. Ketika data model diperbarui, kami akhirnya melewati beberapa lapisan sampai mencapai komponen yang sesuai. Itu menciptakan kopling yang tidak diinginkan antara komponen GUI karena jika kami ingin kelas komponen GUI baru untuk menerima data model, kami harus membuat metode untuk memperbarui data model di semua komponen GUI yang berisi kelas baru.

Untuk memperbaikinya, kami memelihara di kelas Lihat peta referensi untuk semua komponen GUI bersarang sehingga setiap kali data model diperbarui, Lihat dapat mengirim data model yang diperbarui langsung ke komponen GUI yang membutuhkannya, akhir cerita . Ini bekerja dengan baik karena hanya ada satu contoh dari setiap komponen GUI. Saya bisa melihatnya tidak berfungsi dengan baik jika ada beberapa contoh beberapa komponen GUI, membuatnya sulit untuk mengidentifikasi salinan mana yang perlu diperbarui.

David Kaczynski
sumber
0

Apa yang Anda gambarkan adalah yang disebut pola desain Chain Of Responsibility . Apple menggunakan pola ini untuk sistem penanganan acara mereka, untuk apa nilainya.

pengguna8865
sumber