Katakanlah saya memiliki dua jenis objek, A dan B. Hubungan di antara mereka banyak-ke-banyak, tetapi tidak satu pun dari mereka adalah pemilik yang lain.
Kedua instance A dan B harus mewaspadai koneksi; itu bukan hanya satu arah.
Jadi, kita bisa melakukan ini:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
Pertanyaan saya adalah: di mana saya meletakkan fungsi untuk membuat dan menghancurkan koneksi?
Haruskah itu A :: Attach (B), yang kemudian memperbarui A :: Bs dan B :: As vektor?
Atau harus B :: Lampirkan (A), yang tampaknya sama-sama masuk akal.
Tak satu pun dari mereka terasa benar. Jika saya berhenti bekerja dengan kode, dan kembali setelah seminggu, saya yakin saya tidak akan dapat mengingat jika saya harus melakukan A.Attach (B) atau B.Attach (A).
Mungkin itu harus fungsi seperti ini:
CreateConnection(A, B);
Tetapi membuat fungsi global juga tampaknya tidak diinginkan, mengingat itu adalah fungsi khusus untuk bekerja dengan hanya kelas A dan B.
Pertanyaan lain: jika saya sering mengalami masalah / persyaratan ini, bisakah saya membuat solusi umum untuk itu? Mungkin kelas TwoWayConnection yang bisa saya peroleh atau gunakan dalam kelas yang berbagi jenis hubungan ini?
Apa sajakah cara yang baik untuk menangani situasi ini ... Saya tahu cara menangani situasi satu-ke-banyak "C memiliki D" dengan cukup baik, tetapi yang satu ini lebih rumit.
Sunting: Hanya untuk membuatnya lebih eksplisit, pertanyaan ini tidak melibatkan masalah kepemilikan. Baik A dan B dimiliki oleh beberapa objek Z lainnya, dan Z menangani semua masalah kepemilikan. Saya hanya tertarik dengan cara membuat / menghapus tautan banyak-ke-banyak antara A dan B.
Pointer
danGestureRecognizer
. Pointer dimiliki dan dikelola oleh kelas InputManager. GestureRecognizers dimiliki oleh instance Widget, yang pada gilirannya dimiliki oleh instance layar yang dimiliki oleh instance App. Pointer ditugaskan ke GestureRecognizers sehingga mereka dapat mengumpankan data input mentah kepada mereka, tetapi GestureRecognizers perlu mengetahui berapa banyak pointer yang ada saat ini yang dikaitkan dengan mereka (untuk membedakan 1 jari vs 2 gerakan jari, dll.).Jawaban:
Salah satu caranya adalah dengan menambahkan
Attach()
metode publik dan jugaAttachWithoutReciprocating()
metode yang dilindungi ke setiap kelas. BuatA
danB
saling berteman sehinggaAttach()
metode mereka dapat memanggil yang lainAttachWithoutReciprocating()
:Jika Anda menerapkan metode serupa untuk
B
, Anda tidak perlu mengingat kelas mana yang harus dipanggilAttach()
.Saya yakin Anda dapat membungkus perilaku itu dalam
MutuallyAttachable
kelas yang mewarisiA
dan sekaligusB
mewarisi, sehingga menghindari pengulangan dan mencetak poin bonus pada Hari Penghakiman. Tetapi bahkan pendekatan implement-it-di-kedua-tempat yang tidak canggih akan menyelesaikan pekerjaan.sumber
MutuallyAttachable
kelas yang saya tulis untuk tugas ini, jika ada yang ingin menggunakannya kembali: goo.gl/VY9RB (Pastebin) Edit: Kode ini dapat ditingkatkan dengan menjadikannya kelas templat.MutuallyAttachable<T, U>
templat kelas di Gist. gist.github.com/3308058Apakah mungkin bagi hubungan itu sendiri untuk memiliki properti tambahan?
Jika demikian, maka itu harus menjadi kelas yang terpisah.
Jika tidak, maka mekanisme manajemen daftar yang saling berbalas akan cukup.
sumber
Biasanya ketika saya masuk ke situasi seperti ini yang terasa canggung, tetapi saya tidak tahu mengapa, itu karena saya mencoba untuk menempatkan sesuatu di kelas yang salah. Hampir selalu ada cara untuk memindahkan beberapa fungsionalitas keluar dari kelas
A
danB
untuk membuat semuanya lebih jelas.Salah satu kandidat untuk memindahkan asosiasi adalah kode yang menciptakan asosiasi di tempat pertama. Mungkin alih-alih menyebutnya
attach()
hanya menyimpan daftarstd::pair<A, B>
. Jika ada beberapa tempat yang membuat asosiasi itu dapat disarikan menjadi satu kelas.Kandidat lain untuk bergerak adalah objek yang memiliki objek terkait,
Z
dalam kasus Anda.Kandidat lain yang umum untuk memindahkan asosiasi adalah kode yang sering memanggil metode
A
atauB
objek. Misalnya, alih-alih menelepona->foo()
, yang secara internal melakukan sesuatu dengan semuaB
objek terkait , ia sudah tahu asosiasi dan panggilana->foo(b)
untuk masing-masing. Sekali lagi, jika beberapa tempat menyebutnya, ia dapat diabstraksi menjadi satu kelas.Banyak kali objek yang membuat, memiliki, dan menggunakan asosiasi terjadi pada objek yang sama. Itu bisa membuat refactoring sangat mudah.
Metode terakhir adalah untuk mendapatkan hubungan dari hubungan lain. Misalnya, saudara dan saudari adalah hubungan banyak-ke-banyak, tetapi alih-alih menyimpan daftar saudari di kelas saudara dan sebaliknya, Anda memperoleh hubungan itu dari hubungan orang tua. Keuntungan lain dari metode ini adalah ia menciptakan satu sumber kebenaran. Tidak ada cara yang mungkin untuk kesalahan bug atau runtime untuk membuat perbedaan antara daftar saudara, saudari, dan orangtua, karena Anda hanya memiliki satu daftar.
Refactoring semacam ini bukanlah formula ajaib yang berfungsi setiap saat. Anda masih harus bereksperimen sampai Anda menemukan kecocokan untuk setiap keadaan individu, tetapi saya telah menemukan bahwa itu berfungsi sekitar 95% dari waktu. Sisa waktu Anda hanya harus hidup dengan canggung.
sumber
Jika saya menerapkan ini, saya akan meletakkan Lampirkan di kedua A dan B. Di dalam metode Lampirkan, saya kemudian akan memanggil Lampirkan pada objek yang lewat. Dengan begitu Anda dapat memanggil A.Attach (B) atau B.Attach ( SEBUAH). Sintaks saya mungkin tidak benar, saya belum pernah menggunakan c ++ selama bertahun-tahun:
Metode alternatif adalah membuat kelas 3, C yang mengelola daftar statis pasangan AB.
sumber