Kopling longgar dalam Desain Berorientasi Objek

16

Saya mencoba mempelajari GRASP dan saya menemukan ini menjelaskan (di sini di halaman 3 ) tentang Kopling Rendah dan saya sangat terkejut ketika saya menemukan ini:

Pertimbangkan metode addTrackuntuk suatu Albumkelas, dua metode yang mungkin adalah:

addTrack( Track t )

dan

addTrack( int no, String title, double duration )

Metode mana yang mengurangi kopling? Yang kedua tidak, karena kelas yang menggunakan kelas Album tidak harus tahu kelas Track. Secara umum, parameter ke metode harus menggunakan tipe dasar (int, char ...) dan kelas dari paket java. *.

Saya cenderung setuju dengan ini; Saya percaya addTrack(Track t)lebih baik daripada addTrack(int no, String title, double duration)karena berbagai alasan:

  1. Itu selalu lebih baik untuk metode sebagai parameter sesedikit mungkin (menurut Kode Bersih Paman Bob tidak ada atau lebih disukai, 2 dalam beberapa kasus dan 3 dalam kasus khusus; lebih dari 3 kebutuhan refactoring - ini tentu saja rekomendasi bukan aturan holly) .

  2. Jika addTrackmerupakan metode antarmuka, dan persyaratan yang dibutuhkan Trackharus memiliki lebih banyak informasi (misalnya tahun atau genre) maka antarmuka perlu diubah dan agar metode tersebut harus mendukung parameter lain.

  3. Enkapsulasi rusak; jika addTrackada dalam sebuah antarmuka, maka seharusnya tidak tahu internal Track.

  4. Ini sebenarnya lebih digabungkan dengan cara kedua, dengan banyak parameter. Misalkan noparameter perlu diubah dari intmenjadi longkarena ada lebih dari MAX_INTtrek (atau untuk alasan apa pun); maka keduanya Trackdan metode perlu diubah sementara jika metode addTrack(Track track)hanya Trackakan diubah.

Keempat argumen itu sebenarnya terhubung satu sama lain, dan beberapa di antaranya merupakan konsekuensi dari yang lain.

Pendekatan mana yang lebih baik?

m3th0dman
sumber
2
Apakah ini dokumen yang disatukan oleh seorang profesor atau pelatih? Berdasarkan URL tautan yang Anda berikan, sepertinya itu untuk kelas, meskipun saya tidak melihat kredit dalam dokumen tentang siapa yang membuatnya. Jika ini adalah bagian dari kelas, saya sarankan Anda mengajukan pertanyaan-pertanyaan dari orang yang menyediakan dokumen. Ngomong-ngomong, saya setuju dengan alasan Anda - tampaknya bagi saya bahwa suatu kelas Album ingin secara inheren tahu tentang kelas Track.
Derek
Jujur, setiap kali saya membaca tentang "Praktik Terbaik", saya mengambilnya dengan sebutir garam!
AraK
@Derek Saya menemukan dokumen dengan mencari di Google untuk "ambil contoh pola"; Saya tidak tahu siapa yang menulisnya, tetapi karena itu dari universitas, saya yakin itu bisa diandalkan. Saya mencari contoh berdasarkan informasi yang diberikan dan mengabaikan sumbernya.
m3th0dman
4
@ m3th0dman "tapi karena itu dari universitas, saya yakin itu bisa diandalkan." Bagi saya karena berasal dari universitas, saya menganggapnya tidak dapat diandalkan. Saya tidak percaya pada seseorang yang belum bekerja pada proyek multi-tahun berbicara tentang praktik terbaik dalam pengembangan perangkat lunak.
Arak
1
@AraK Andal tidak berarti tidak perlu dipertanyakan; dan itulah yang saya lakukan di sini, mempertanyakannya.
m3th0dman

Jawaban:

15

Nah, tiga poin pertama Anda sebenarnya tentang prinsip-prinsip lain selain penggabungan. Anda selalu harus menemukan keseimbangan antara prinsip-prinsip desain yang sering bertentangan.

Poin keempat Anda adalah tentang pemasangan, dan saya sangat setuju dengan Anda. Coupling adalah tentang aliran data antar modul. Jenis wadah yang mengalir data sebagian besar tidak material. Melewati durasi sebagai gandanya bukan sebagai bidang Tracktidak menghilangkan kebutuhan untuk melewatinya. Modul masih perlu berbagi jumlah data yang sama, dan masih memiliki jumlah kopling yang sama.

Dia juga gagal untuk mempertimbangkan semua kopling dalam sistem sebagai agregat. Sementara memperkenalkan Trackkelas diakui menambah ketergantungan lain antara dua modul individu, itu dapat secara signifikan mengurangi kopling sistem , yang merupakan ukuran penting di sini.

Sebagai contoh, mempertimbangkan "Tambahkan ke Daftar Putar" tombol dan Playlistobjek. Memperkenalkan Trackobjek dapat dianggap meningkatkan kopling jika Anda hanya mempertimbangkan kedua objek tersebut. Anda sekarang memiliki tiga kelas yang saling tergantung bukan dua. Namun, itu bukan keseluruhan sistem Anda. Anda juga perlu mengimpor trek, memutar trek, menampilkan trek, dll. Menambahkan satu kelas lagi ke campuran itu dapat diabaikan.

Sekarang pertimbangkan untuk menambahkan dukungan untuk memainkan trek melalui jaringan, bukan hanya secara lokal. Anda hanya perlu membuat NetworkTrackobjek yang sesuai dengan antarmuka yang sama. Tanpa Trackobjek, Anda harus membuat fungsi di mana-mana seperti:

addNetworkTrack(int no, string title, double duration, URL location)

Itu secara efektif menggandakan kopling Anda, membutuhkan bahkan modul yang tidak peduli tentang hal-hal khusus jaringan untuk tetap melacaknya, agar dapat meneruskannya.

Tes efek riak Anda bagus untuk menentukan jumlah kopling Anda yang sebenarnya. Yang kami khawatirkan adalah membatasi tempat-tempat yang terkena dampak perubahan.

Karl Bielefeldt
sumber
1
+ Kopling ke primitif masih kopling tidak peduli bagaimana itu diiris.
JustinC
+1 untuk menyebutkan opsi tambahkan efek URL / riak.
user949300
4
+1 yang menarik untuk dibaca di sini juga akan menjadi pembahasan tentang Prinsip Ketergantungan Inversi di DIP di alam liar di mana penggunaan tipe primitif sebenarnya dipandang sebagai "bau" Obsesi Primitif dengan "Object Value" sebagai perbaikannya. Bagi saya yang terdengar seperti itu akan lebih baik untuk melewatkan objek Track yang seluruh jenis primitif ... Dan jika Anda ingin menghindari ketergantungan pada / digabungkan dengan kelas-kelas tertentu, gunakan antarmuka.
Marjan Venema
Jawaban yang diterima karena penjelasan yang bagus tentang perbedaan antara kopling sistem total dan kopling modul.
m3th0dman
10

Rekomendasi saya adalah:

Menggunakan

addTrack( ITrack t )

tapi pastikan itu ITrackadalah antarmuka dan bukan kelas yang konkret.

Album tidak tahu internal ITrackimplementors. Ini hanya digabungkan dengan kontrak yang ditentukan oleh ITrack.

Saya pikir ini adalah solusi yang menghasilkan jumlah kopling paling sedikit.

Tulains Córdova
sumber
1
Saya percaya bahwa Track hanyalah objek transfer data / kacang yang sederhana, di mana Track hanya memiliki bidang dan pengambil / setter di atasnya; Apakah diperlukan antarmuka dalam hal ini?
m3th0dman
6
Yg dibutuhkan? Mungkin tidak. Sugestif, ya. Arti konkret dari sebuah trek dapat dan akan berevolusi, tetapi apa yang dibutuhkan oleh kelas pengonsumsi mungkin tidak.
JustinC
2
@ m3th0dman Selalu bergantung pada abstraksi, bukan pada konkret. Itu berlaku terlepas dari Trackmenjadi bodoh atau pintar. Trackadalah konkret. ITrackantarmuka adalah abstraksi. Dengan begitu Anda akan dapat memiliki berbagai jenis Trek di masa mendatang, selama mereka mematuhinya ITrack.
Tulains Córdova
4
Saya setuju dengan gagasan itu, tetapi kehilangan awalan 'Saya'. Dari Clean Code, oleh Robert Martin, halaman 24: "Aku sebelumnya, yang begitu umum dalam gumpalan warisan hari ini, adalah gangguan terbaik dan terlalu banyak informasi terburuk. Saya tidak ingin pengguna saya tahu bahwa saya menyerahkan kepada mereka antarmuka. "
Benjamin Brumfield
1
@BenjaminBrumfield Kamu benar. Saya juga tidak suka awalan, meskipun saya akan meninggalkan jawaban untuk kejelasan.
Tulains Córdova
4

Saya berpendapat bahwa metode contoh kedua kemungkinan besar meningkatkan kopling, karena kemungkinan besar adalah instantiating objek Track dan menyimpannya dalam objek Album saat ini. (Seperti yang disarankan dalam komentar saya di atas, saya akan menganggapnya melekat bahwa kelas Album akan memiliki konsep kelas Track di suatu tempat di dalamnya.)

Metode contoh pertama mengasumsikan bahwa suatu Track sedang dipakai di luar kelas Album, jadi setidaknya, kita dapat mengasumsikan bahwa instantiasi dari kelas Track tidak digabungkan ke kelas Album.

Jika praktik terbaik menyarankan bahwa kita tidak pernah memiliki satu referensi kelas kelas kedua, keseluruhan pemrograman berorientasi objek akan dibuang keluar jendela.

Derek
sumber
Saya tidak melihat bagaimana memiliki referensi implisit ke kelas lain membuatnya lebih digabungkan daripada memiliki referensi eksplisit. Either way, dua kelas digabungkan. Saya pikir lebih baik untuk membuat kopling menjadi eksplisit, tetapi saya tidak berpikir itu "lebih" ditambah cara baik.
TMN
1
@ TMN, kopling tambahan adalah bagaimana saya menyiratkan bahwa contoh kedua mungkin akan berakhir secara internal membuat objek Track baru. Instansiasi objek sedang digabungkan ke metode yang seharusnya hanya menambahkan objek Track ke beberapa jenis daftar di objek Album (melanggar Prinsip Tanggung Jawab Tunggal). Jika cara Track dibuat harus diubah, metode addTrack () juga perlu diubah. Ini tidak demikian halnya dengan contoh pertama.
Derek
3

Coupling hanyalah salah satu dari banyak aspek untuk mencoba mendapatkan dalam kode Anda. Dengan mengurangi sambungan, Anda tidak perlu meningkatkan program Anda. Secara umum, ini adalah praktik terbaik, tetapi dalam contoh khusus ini, mengapa tidak Trackdiketahui?

Dengan menggunakan Trackkelas untuk diteruskan Album, Anda membuat kode Anda lebih mudah dibaca, tetapi yang lebih penting, seperti yang Anda sebutkan, Anda mengubah daftar parameter statis menjadi objek dinamis. Itu pada akhirnya membuat antarmuka Anda jauh lebih dinamis.

Anda menyebutkan bahwa enkapsulasi rusak, tetapi tidak. Albumharus mengetahui bagian dalam Track, dan jika Anda tidak menggunakan objek, Albumharus mengetahui setiap informasi yang diteruskan sebelum dapat memanfaatkannya semua sama. Penelepon harus mengetahui internal Trackjuga, karena ia harus membangun Trackobjek, tetapi penelepon harus mengetahui informasi ini semua sama jika diteruskan langsung ke metode. Dengan kata lain, jika keuntungan enkapsulasi adalah tidak mengetahui isi suatu objek, itu tidak mungkin digunakan dalam kasus ini karena Albumharus memanfaatkan Trackinformasi sama saja.

Di mana Anda tidak ingin menggunakan Trackadalah jika Trackberisi logika internal yang Anda tidak ingin pemanggil memiliki akses. Dengan kata lain, jika Albumkelas yang digunakan seorang programmer menggunakan perpustakaan Anda, Anda tidak ingin dia menggunakannya Trackjika Anda mengatakannya, panggil metode untuk bertahan di database. Masalah sebenarnya dengan ini terletak pada kenyataan bahwa antarmuka terjerat dengan model.

Untuk memperbaiki masalah, Anda harus memisahkan Trackkomponen antarmuka dan komponen logikanya, membuat dua kelas terpisah. Untuk penelepon, Trackmenjadi kelas ringan yang dimaksudkan untuk menyimpan informasi dan menawarkan optimasi kecil (data yang dihitung dan / atau nilai default). Di dalam Album, Anda akan menggunakan kelas bernama TrackDAOuntuk melakukan angkat berat yang terkait dengan menyimpan informasi dari Trackke database.

Tentu saja, ini hanya sebuah contoh. Saya yakin ini bukan kasus Anda sama sekali, jadi jangan ragu untuk menggunakan Trackrasa bersalah. Ingatlah untuk selalu mengingat penelepon Anda ketika Anda sedang membangun kelas dan untuk membuat antarmuka bila diperlukan.

Neil
sumber
3

Keduanya benar

addTrack( Track t ) 

adalah lebih baik (karena Anda sudah argumented) sementara

addTrack( int no, String title, double duration ) 

adalah kurang ditambah karena kode yang menggunakan addTracktidak perlu tahu bahwa ada Trackkelas. Track dapat diubah namanya misalnya tanpa perlu memperbarui kode panggilan.

Sementara Anda berbicara tentang kode yang lebih mudah dibaca / dipelihara, artikel ini berbicara tentang pemasangan . Kode kurang digabungkan tidak selalu lebih mudah untuk diterapkan dan dipahami.

k3b
sumber
Lihat argumen 4; Saya tidak melihat bagaimana yang kedua kurang digabungkan.
m3th0dman
3

Kopling Rendah tidak berarti Kopling Tidak . Sesuatu, di suatu tempat, harus tahu tentang objek di tempat lain dalam basis kode, dan semakin Anda mengurangi ketergantungan pada objek "kustom", semakin banyak alasan yang Anda berikan untuk mengubah kode. Apa yang penulis kutip adalah mempromosikan dengan fungsi kedua kurang digabungkan, tetapi juga kurang berorientasi objek, yang bertentangan dengan seluruh gagasan GRASP sebagai metodologi desain berorientasi objek . Intinya adalah bagaimana merancang sistem sebagai kumpulan objek dan interaksinya; Menghindarinya sama seperti mengajari Anda cara mengendarai mobil dengan mengatakan Anda harus mengendarai sepeda saja.

Sebagai gantinya, jalan yang tepat adalah untuk mengurangi ketergantungan pada benda - benda konkret , yang merupakan teori "lepas kopling". Semakin sedikit jenis beton tertentu yang harus dimiliki suatu metode, semakin baik. Hanya dengan pernyataan itu, opsi pertama sebenarnya kurang digabungkan, karena metode kedua mengambil jenis yang lebih sederhana harus tahu tentang semua jenis yang lebih sederhana. Tentu mereka built-in, dan kode di dalam metode mungkin harus peduli, tetapi tanda tangan metode dan penelepon metode itu pasti tidak . Mengubah salah satu dari parameter ini yang berkaitan dengan trek audio konseptual akan membutuhkan lebih banyak perubahan ketika terpisah dibandingkan ketika mereka terkandung dalam objek Track (yang merupakan titik objek; enkapsulasi).

Selangkah lebih maju, jika Track diharapkan akan diganti dengan sesuatu yang melakukan pekerjaan yang sama dengan lebih baik, mungkin sebuah antarmuka yang mendefinisikan fungsionalitas yang diperlukan adalah dalam urutan, sebuah ITrack. Itu dapat memungkinkan untuk implementasi yang berbeda seperti "AnalogTrack", "CdTrack" dan "Mp3Track" yang memberikan informasi tambahan yang lebih spesifik untuk format-format tersebut, sambil tetap memberikan paparan data dasar dari ITrack yang secara konseptual mewakili "track"; sub-bagian audio yang terbatas. Track juga bisa menjadi kelas dasar abstrak, tetapi ini mengharuskan Anda untuk selalu ingin menggunakan implementasi yang melekat dalam Track; implementasikan kembali sebagai BetterTrack dan sekarang Anda harus mengubah parameter yang diharapkan.

Demikianlah aturan emasnya; program dan komponen kode mereka akan selalu memiliki alasan untuk berubah. Anda tidak dapat menulis program yang tidak akan memerlukan kode pengeditan yang telah Anda tulis untuk menambahkan sesuatu yang baru atau mengubah perilakunya. Tujuan Anda, dalam metodologi (GRASP, SOLID, setiap akronim atau kata kunci yang dapat Anda pikirkan) hanya untuk mengidentifikasi hal-hal yang akan harus berubah dari waktu ke waktu, dan merancang sistem sehingga perubahan tersebut adalah sebagai mudah untuk membuat mungkin (Diterjemahkan; menyentuh beberapa baris kode dan memengaruhi sesedikit mungkin area lain dari sistem di luar cakupan perubahan yang Anda maksudkan). Contoh kasus, apa yang paling mungkin berubah adalah Track akan mendapatkan lebih banyak anggota data yang addTrack () mungkin peduli atau tidak peduli, tidak Track itu akan diganti dengan BetterTrack.

KeithS
sumber