Mohon maaf jika "Komposisi Hierarki" bukan sesuatu, tapi saya akan menjelaskan apa yang saya maksud dengan itu dalam pertanyaan.
Tidak ada programmer OO yang belum menemukan variasi "Pertahankan hierarki warisan tetap" atau "Lebih suka komposisi daripada warisan" dan sebagainya. Namun, hierarki komposisi yang mendalam juga tampak bermasalah.
Katakanlah kita membutuhkan kumpulan laporan yang merinci hasil percobaan:
class Model {
// ... interface
Array<Result> m_results;
}
Setiap hasil memiliki sifat tertentu. Ini termasuk waktu percobaan, serta beberapa meta-data dari setiap tahap percobaan:
enum Stage {
Pre = 1,
Post
};
class Result {
// ... interface
Epoch m_epoch;
Map<Stage, ExperimentModules> m_modules;
}
OK bagus. Sekarang, setiap modul eksperimen memiliki string yang menggambarkan hasil percobaan, serta kumpulan referensi ke set sampel eksperimental:
class ExperimentalModules {
// ... interface
String m_reportText;
Array<Sample> m_entities;
}
Dan kemudian masing-masing sampel memiliki ... well, Anda mendapatkan gambar.
Masalahnya adalah bahwa jika saya memodelkan objek dari domain aplikasi saya, ini sepertinya sangat alami, tetapi pada akhirnya, a Result
hanyalah sebuah wadah data yang bodoh! Tampaknya tidak ada gunanya membuat kelompok besar kelas untuk itu.
Dengan asumsi bahwa struktur data dan kelas yang ditunjukkan di atas dengan benar memodelkan hubungan dalam domain aplikasi, apakah ada cara yang lebih baik untuk memodelkan "hasil" seperti itu, tanpa menggunakan hierarki komposisi yang mendalam? Apakah ada konteks eksternal yang akan membantu Anda menentukan apakah desain seperti itu bagus atau tidak?
sumber
Jawaban:
Inilah yang menjadi tujuan Hukum Demeter / prinsip pengetahuan terendah. Seorang pengguna yang
Model
seharusnya tidak perlu harus tahu tentangExperimentalModules
'antarmuka untuk melakukan pekerjaan mereka.Masalah umum adalah bahwa ketika suatu kelas mewarisi dari tipe lain atau memiliki bidang publik dengan beberapa tipe atau ketika suatu metode mengambil tipe sebagai parameter atau mengembalikan suatu tipe, antarmuka semua tipe itu menjadi bagian dari antarmuka efektif kelas saya. Pertanyaannya menjadi: tipe-tipe inilah yang saya andalkan: apakah menggunakan semua itu inti dari kelas saya? Atau mereka hanya detail implementasi yang tidak sengaja saya ketahui? Karena jika mereka adalah detail implementasi, saya baru saja mengekspos mereka dan memungkinkan pengguna kelas saya untuk bergantung padanya - klien saya sekarang sangat dekat dengan detail implementasi saya.
Jadi jika saya ingin meningkatkan kohesi, saya dapat membatasi kopling ini dengan menulis lebih banyak metode yang melakukan hal-hal yang bermanfaat, daripada mengharuskan pengguna untuk menjangkau ke dalam struktur pribadi saya. Di sini, kami mungkin menawarkan metode untuk mencari atau memfilter hasil. Itu membuat kelas saya lebih mudah untuk refactor, karena saya sekarang dapat mengubah representasi internal tanpa merusak antarmuka saya.
Tapi ini bukan tanpa biaya - antarmuka kelas saya sekarang menjadi membengkak dengan operasi yang tidak selalu diperlukan. Ini melanggar Prinsip Segregasi Antarmuka: Saya tidak boleh memaksa pengguna untuk bergantung pada lebih banyak operasi daripada yang sebenarnya mereka gunakan. Dan di situlah desain menjadi penting: Desain adalah tentang menentukan prinsip mana yang lebih penting dalam konteks Anda, dan tentang menemukan kompromi yang sesuai.
Saat mendesain pustaka yang harus menjaga kompatibilitas sumber di berbagai versi, saya akan lebih cenderung mengikuti prinsip pengetahuan paling tidak lebih hati-hati. Jika perpustakaan saya menggunakan perpustakaan lain secara internal, saya tidak akan pernah mengekspos apa pun yang didefinisikan oleh perpustakaan itu kepada pengguna saya. Jika saya perlu mengekspos antarmuka dari perpustakaan internal, saya akan membuat objek pembungkus sederhana. Pola Desain seperti Pola Jembatan atau Pola Fasad dapat membantu di sini.
Tetapi jika antarmuka saya hanya digunakan secara internal, atau jika antarmuka yang saya paparkan sangat mendasar (bagian dari perpustakaan standar, atau perpustakaan yang sangat mendasar sehingga tidak akan pernah masuk akal untuk mengubahnya), maka mungkin tidak ada gunanya menyembunyikannya antarmuka ini. Seperti biasa, tidak ada jawaban satu ukuran untuk semua.
sumber
Tentu saja. Aturan sebenarnya harus mengatakan "jaga semua hierarki selurus mungkin."
Tetapi hierarki komposisi yang dalam lebih tidak rapuh dibandingkan hierarki warisan yang dalam, dan lebih fleksibel untuk berubah. Secara intuitif, ini seharusnya tidak mengejutkan; hierarki keluarga juga demikian. Hubungan Anda dengan kerabat darah Anda tetap dalam genetika; hubungan Anda dengan teman dan pasangan Anda tidak.
Teladan Anda tidak mengejutkan saya sebagai "hierarki yang mendalam." Suatu bentuk mewarisi dari persegi panjang yang mewarisi dari set poin yang mewarisi dari koordinat x dan y, dan saya bahkan belum mendapatkan kepala penuh uap belum.
sumber
Mengutip Einstein:
Berarti jika masalah yang Anda modelkan adalah seperti itu, Anda seharusnya tidak memiliki keraguan tentang memodelkannya apa adanya.
Jika aplikasinya hanya spreadsheet raksasa, maka Anda tidak perlu memodelkan hierarki komposisi seperti apa adanya. Kalau tidak, lakukanlah. Saya tidak berpikir hal itu sangat dalam. Simpan saja getter yang mengembalikan koleksi malas jika mengisi koleksi seperti itu mahal, seperti meminta mereka ke repositori yang menarik mereka membentuk database. Maksud saya malas, jangan mengisi mereka sampai mereka dibutuhkan.
sumber
Ya, Anda bisa memodelkannya seperti tabel relasional
dll. yang sering mengarah pada pemrograman yang lebih mudah dan pemetaan sederhana ke basis data. tetapi dengan mengorbankan kehilangan pengkodean keras hubungan Anda
sumber
Saya tidak benar-benar tahu bagaimana menyelesaikan ini di Jawa, karena Java dibangun di sekitar gagasan bahwa semuanya adalah objek. Anda tidak dapat menekan kelas menengah dengan menggantinya dengan kamus karena tipe dalam gumpalan data Anda beragam (seperti cap waktu + daftar, atau string + daftar ...). Alternatif untuk mengganti kelas kontainer dengan bidangnya (misalnya meneruskan variabel "m_epoch" dengan "m_modules") benar-benar buruk, karena ia membuat objek implisit (Anda tidak dapat memiliki satu tanpa yang lain) tanpa ada yang khusus manfaat.
Dalam bahasa predileksi saya (Python), aturan praktis saya adalah untuk tidak membuat objek jika tidak ada logika tertentu (kecuali dasar dan pengaturan) miliknya. Tetapi mengingat kendala Anda, saya pikir hierarki komposisi yang Anda miliki adalah desain terbaik.
sumber