Bukankah "hierarki komposisi yang dalam" juga buruk?

19

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 Resulthanyalah 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?

Luar Biasa
sumber
Tidak mengerti bagian konteks eksternal.
Tulains Córdova
@ TulainsCórdova yang ingin saya tanyakan di sini adalah "adakah situasi di mana komposisi yang dalam merupakan solusi yang baik"?
AwesomeSauce
Saya menambahkan jawaban.
Tulains Córdova
5
Ya persis. Masalah yang orang coba perbaiki dengan mengganti warisan dengan komposisi tidak secara ajaib hilang hanya karena Anda mengubah satu strategi agregasi fitur untuk yang lain. Keduanya merupakan solusi untuk mengelola kompleksitas, dan kompleksitas itu masih ada dan harus ditangani dengan cara tertentu.
Mason Wheeler
3
Saya tidak melihat ada yang salah dengan model data yang mendalam. Tapi saya juga tidak melihat bagaimana Anda bisa memodelkan ini dengan warisan karena ini adalah hubungan 1: N di setiap lapisan.
usr

Jawaban:

17

Inilah yang menjadi tujuan Hukum Demeter / prinsip pengetahuan terendah. Seorang pengguna yang Modelseharusnya tidak perlu harus tahu tentang ExperimentalModules'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.

amon
sumber
Anda menyebutkan kekhawatiran utama yang saya alami; gagasan bahwa saya harus menjangkau jauh ke dalam implementasi untuk melakukan sesuatu yang bermanfaat pada tingkat abstraksi yang lebih tinggi. Diskusi yang sangat berguna, terima kasih.
AwesomeSauce
7

Bukankah "hierarki komposisi yang dalam" juga buruk?

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.

Robert Harvey
sumber
4

Mengutip Einstein:

jaga agar semua hierarki serata mungkin, tetapi tidak datar

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.

Tulains Córdova
sumber
1

Ya, Anda bisa memodelkannya seperti tabel relasional

Result {
    Id
    ModelId
    Epoch
}

dll. yang sering mengarah pada pemrograman yang lebih mudah dan pemetaan sederhana ke basis data. tetapi dengan mengorbankan kehilangan pengkodean keras hubungan Anda

Ewan
sumber
0

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.

Arthur Havlicek
sumber