Saya mengembangkan model objek yang memiliki banyak kelas orangtua / anak yang berbeda. Setiap objek anak memiliki referensi ke objek induknya. Saya dapat memikirkan (dan telah mencoba) beberapa cara untuk menginisialisasi referensi orang tua, tetapi saya menemukan kelemahan yang signifikan untuk setiap pendekatan. Mengingat pendekatan yang dijelaskan di bawah ini mana yang terbaik ... atau apa yang lebih baik.
Saya tidak akan memastikan kode di bawah ini dikompilasi jadi silakan coba lihat maksud saya jika kode tidak benar secara sintaksis.
Perhatikan bahwa beberapa konstruktor kelas anak saya mengambil parameter (selain orang tua) meskipun saya tidak selalu menampilkannya.
Penelepon bertanggung jawab untuk mengatur orang tua dan menambahkan ke orang tua yang sama.
class Child { public Child(Parent parent) {Parent=parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; set;} //children private List<Child> _children = new List<Child>(); public List<Child> Children { get {return _children;} } }
Kelemahan: pengaturan induk adalah proses dua langkah untuk konsumen.
var child = new Child(parent); parent.Children.Add(child);
Kelemahan: rawan kesalahan. Penelepon dapat menambahkan anak ke orangtua yang berbeda dari yang digunakan untuk menginisialisasi anak.
var child = new Child(parent1); parent2.Children.Add(child);
Induk memverifikasi bahwa pemanggil menambahkan anak ke induk yang diinisialisasi.
class Child { public Child(Parent parent) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { if (value.Parent != this) throw new Exception(); _child=value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { if (child.Parent != this) throw new Exception(); _children.Add(child); } }
Kelemahan: Pemanggil masih memiliki proses dua langkah untuk pengaturan induk.
Kelemahan: pengecekan runtime - mengurangi kinerja dan menambahkan kode ke setiap add / setter.
Orang tua menetapkan referensi orang tua anak (untuk dirinya sendiri) ketika anak ditambahkan / ditugaskan ke orang tua. Setter induk adalah internal.
class Child { public Parent Parent {get; internal set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { value.Parent = this; _child = value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { child.Parent = this; _children.Add(child); } }
Kelemahan: Anak dibuat tanpa referensi orang tua. Terkadang inisialisasi / validasi memerlukan induk yang berarti beberapa inisialisasi / validasi harus dilakukan di setter induk anak. Kode dapat menjadi rumit. Akan jauh lebih mudah untuk menerapkan anak jika selalu memiliki referensi orang tuanya.
Orang tua memperlihatkan metode penambahan pabrik sehingga seorang anak selalu memiliki referensi orang tua. Aktor anak adalah internal. Setter induk bersifat pribadi.
class Child { internal Child(Parent parent, init-params) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; private set;} public void CreateChild(init-params) { var child = new Child(this, init-params); Child = value; } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public Child AddChild(init-params) { var child = new Child(this, init-params); _children.Add(child); return child; } }
Kelemahan: Tidak dapat menggunakan sintaks inisialisasi seperti
new Child(){prop = value}
. Sebaliknya harus dilakukan:var c = parent.AddChild(); c.prop = value;
Kelemahan: Harus menduplikasi parameter konstruktor anak dalam metode pabrik tambahan.
Kelemahan: Tidak dapat menggunakan properti setter untuk anak tunggal. Tampaknya lumpuh bahwa saya memerlukan metode untuk mengatur nilai tetapi memberikan akses baca melalui pengambil properti. Itu miring.
Child menambahkan dirinya ke orang tua yang dirujuk dalam konstruktornya. Ctor anak adalah publik. Tidak ada publik menambahkan akses dari orang tua.
//singleton class Child{ public Child(ParentWithChild parent) { Parent = parent; Parent.Child = this; } public ParentWithChild Parent {get; private set;} } class ParentWithChild { public Child Child {get; internal set;} } //children class Child { public Child(ParentWithChildren parent) { Parent = parent; Parent._children.Add(this); } public ParentWithChildren Parent {get; private set;} } class ParentWithChildren { internal List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } }
Kelemahan: sintaks panggilan tidak bagus. Biasanya seseorang memanggil
add
metode pada orang tua alih-alih hanya membuat objek seperti ini:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
Dan menetapkan properti daripada hanya membuat objek seperti ini:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Saya baru tahu bahwa SE tidak mengizinkan beberapa pertanyaan subjektif dan jelas ini adalah pertanyaan subjektif. Tapi, mungkin itu adalah pertanyaan subjektif yang bagus.
sumber
Jawaban:
Saya akan menjauh dari skenario apa pun yang mengharuskan anak untuk tahu tentang orang tua.
Ada cara menyampaikan pesan dari anak ke orang tua melalui acara. Dengan cara ini orang tua, setelah menambahkan, cukup mendaftar ke acara yang dipicu anak, tanpa anak harus tahu tentang orang tua secara langsung. Lagipula, ini kemungkinan adalah penggunaan yang dimaksudkan dari seorang anak yang mengetahui tentang orang tuanya, agar dapat menggunakan orang tuanya untuk beberapa efek. Kecuali Anda tidak ingin anak melakukan pekerjaan orang tua, jadi sebenarnya yang ingin Anda lakukan hanyalah memberi tahu orang tua bahwa sesuatu telah terjadi. Karena itu, apa yang perlu Anda tangani adalah peristiwa pada anak, yang dapat dimanfaatkan orang tua.
Pola ini juga berskala sangat baik jika acara ini bermanfaat bagi kelas-kelas lain. Mungkin ini sedikit berlebihan, tetapi juga mencegah Anda menembak diri sendiri nanti, karena tergoda untuk ingin menggunakan orang tua di kelas anak Anda yang hanya menyandingkan dua kelas bahkan lebih. Refactoring kelas-kelas semacam itu sesudahnya memakan waktu dan dapat dengan mudah membuat bug di program Anda.
Semoga itu bisa membantu!
sumber
Child
) mempertahankan seperangkat pengamat (Parent
), yang memperkenalkan kembali sirkularitas dengan cara mundur (dan memperkenalkan sejumlah masalah sendiri).Saya pikir pilihan Anda 3 mungkin yang paling bersih. Kau menulis.
Saya tidak melihat itu sebagai downside. Bahkan, desain program Anda dapat mengambil manfaat dari objek anak yang dapat dibuat tanpa orang tua terlebih dahulu. Misalnya, ini dapat membuat pengujian anak-anak dalam isolasi jauh lebih mudah. Jika pengguna model Anda lupa untuk menambahkan anak ke induknya dan memanggil metode yang mengharapkan properti induk diinisialisasi di kelas anak Anda, maka ia mendapat pengecualian ref kosong - yang persis apa yang Anda inginkan: crash awal untuk kesalahan pemakaian.
Dan jika Anda berpikir seorang anak membutuhkan atribut induknya untuk diinisialisasi dalam konstruktor dalam semua keadaan karena alasan teknis, gunakan sesuatu seperti "objek orangtua nol" sebagai nilai default (meskipun ini memiliki risiko kesalahan masking).
sumber
SetParent
metode yang baik perlu mendukung mengubah asal usul anak yang ada (yang mungkin sulit dilakukan dan / atau tidak masuk akal) atau hanya akan dipanggil sekali. Membuat model situasi seperti kelompok agregasi (seperti yang dikemukakan Mike Brown) bisa jauh lebih baik daripada memiliki anak yang mulai tanpa orangtua.Tidak ada yang mencegah kohesi tinggi antara dua kelas yang biasanya digunakan bersama-sama (mis. Orde dan LineItem umumnya akan saling referensi). Namun, dalam kasus ini, saya cenderung mematuhi aturan Desain Berbasis Domain dan memodelkannya sebagai Agregat dengan Orang Tua sebagai Agregat Root. Ini memberi tahu kita bahwa AR bertanggung jawab atas masa pakai semua objek dalam agregatnya.
Jadi akan seperti skenario empat Anda di mana orang tua memperlihatkan metode untuk membuat anak-anaknya menerima parameter yang diperlukan untuk menginisialisasi anak-anak dengan benar dan menambahkannya ke koleksi.
sumber
Saya akan menyarankan memiliki objek "pabrik anak" yang dilewatkan ke metode induk yang menciptakan anak (menggunakan objek "pabrik anak"), melampirkannya, dan mengembalikan tampilan. Objek anak itu sendiri tidak akan pernah terpapar di luar orangtua. Pendekatan ini dapat bekerja dengan baik untuk hal-hal seperti simulasi. Dalam simulasi elektronik, satu objek "pabrik anak" mungkin mewakili spesifikasi untuk beberapa jenis transistor; yang lain mungkin mewakili spesifikasi untuk resistor; sirkuit yang membutuhkan dua transistor dan empat resistor dapat dibuat dengan kode seperti:
Perhatikan bahwa simulator tidak perlu mempertahankan referensi apa pun ke objek pencipta anak, dan
AddComponent
tidak akan mengembalikan referensi ke objek yang dibuat dan disimpan oleh simulator, melainkan objek yang mewakili tampilan. JikaAddComponent
metode ini generik, objek tampilan dapat menyertakan fungsi spesifik komponen tetapi tidak akan mengekspos anggota yang digunakan orang tua untuk mengelola lampiran.sumber
Daftar hebat. Saya tidak tahu metode mana yang "terbaik" tetapi di sini ada satu untuk menemukan metode yang paling ekspresif.
Mulailah dengan kelas orangtua dan anak yang paling sederhana. Tulis kode Anda dengan itu. Setelah Anda melihat duplikasi kode yang dapat dinamai Anda memasukkannya ke dalam metode.
Mungkin Anda mengerti
addChild()
. Mungkin Anda mendapatkan sesuatu sepertiaddChildren(List<Child>)
atauaddChildrenNamed(List<String>)
atauloadChildrenFrom(String)
ataunewTwins(String, String)
atauChild.replicate(int)
.Jika masalah Anda adalah tentang memaksakan hubungan satu-ke-banyak, mungkin Anda harus melakukannya
Ini bukan jawaban tapi saya harap Anda menemukannya saat membaca ini.
sumber
Saya menghargai bahwa memiliki tautan dari anak ke orang tua memiliki kelemahan seperti yang disebutkan di atas.
Namun untuk banyak skenario, penyelesaian dengan peristiwa dan mekanisme "terputus" lainnya juga membawa kerumitan mereka sendiri dan garis kode tambahan.
Misalnya mengangkat suatu acara dari Anak untuk diterima oleh Orang Tua itu akan mengikat keduanya bersama-sama meskipun dalam mode kopling longgar.
Mungkin untuk banyak skenario, jelas bagi semua pengembang apa arti properti Child.Parent. Untuk sebagian besar sistem yang saya kerjakan ini bekerja dengan baik. Over engineering bisa memakan waktu dan…. Membingungkan!
Have a Parent.AttachChild () metode yang melakukan semua pekerjaan yang diperlukan untuk mengikat Anak ke orang tuanya. Semua orang jelas tentang apa artinya "ini"
sumber