Alternatif untuk Lajang / global


16

Saya telah mendengar berkali-kali tentang jebakan para Singleton / global, dan saya mengerti mengapa mereka begitu disukai.

Yang tidak saya mengerti adalah apa alternatif yang elegan dan tidak berantakan itu. Tampaknya alternatif untuk menggunakan Singletons / globals selalu melibatkan melewati objek satu juta level ke bawah melalui objek mesin Anda hingga mencapai objek yang membutuhkannya.

Misalnya, dalam game saya, saya memuat beberapa aset saat game dimulai. Aset ini tidak digunakan sampai nanti ketika pemain menavigasi menu utama dan memasuki permainan. Apakah saya harus meneruskan data ini dari objek Game saya, ke objek ScreenManager saya (terlepas dari kenyataan bahwa hanya satu Layar yang benar-benar peduli tentang data ini), kemudian ke objek Layar yang sesuai, dan di mana pun?

Sepertinya saya memperdagangkan data keadaan global untuk injeksi dependensi yang berantakan, meneruskan data ke objek yang bahkan tidak peduli dengan data kecuali untuk tujuan meneruskannya ke objek anak.

Apakah ini kasus di mana Singleton akan menjadi hal yang baik, atau adakah solusi elegan yang saya lewatkan?

Jawaban:


16

Jangan mengacaukan lajang dan global. Sementara beberapa jenis variabel global biasanya diperlukan, singleton bukan hanya pengganti untuk variabel global, tetapi terutama cara untuk mengatasi masalah urutan inisialisasi statis dalam C ++ ( dan FQA ). (Dalam bahasa lain, ini adalah cara untuk mengatasi kekurangan bahasa yang berbeda, seperti kurangnya variabel global dan fungsi telanjang.)

Jika Anda bisa menggunakan pointer global alih-alih singleton, dan memastikan itu diinisialisasi (secara manual) sebelum apa pun membutuhkannya, Anda menghindari pemanggilan fungsi dan overhead cabang, sintaks yang lumpuh untuk mendapatkan objek, dan Anda benar-benar dapat membuat instance kedua kelas ketika Anda perlu untuk tes atau karena desain Anda berubah.

Untuk beberapa variabel global yang Anda inginkan (contoh umum adalah keluaran audio, daftar jendela terbuka, pengendali keyboard, dll.), Saya merekomendasikan pola pencari lokasi layanan . Ini membuatnya mudah untuk mengganti hal-hal dengan implementasi yang berbeda (misalnya perangkat audio nyata vs nol), dan mengumpulkan semua global Anda ke dalam satu struktur untuk menghindari mencemari namespace Anda.


+1. Jawaban dan terima kasih yang baik untuk tautan pola pelacak layanan. Itu bacaan yang sangat menarik.
bummzack

1

Jika Anda tidak / tidak dapat memiliki satu bagian kode yang secara ajaib "tahu" tentang beberapa data, maka entah bagaimana itu harus diteruskan. Namun itu tidak berarti harus melalui argumen saja.

Dalam contoh kasus Anda, bisakah Anda tidak memiliki semacam "AssetManager" yang akan memuat dan menyimpan aset, dan kemudian ScreenManager hanya perlu diberikan referensi untuk itu (mungkin saat pembuatan)? Dalam pengertian itu Anda meneruskan referensi ke aset yang dibungkus dalam objek lain, dan Anda bisa memberikannya sekali, pada inisialisasi, daripada meneruskannya ke fungsi daun saat diperlukan.

Sekarang IMHO bahwa AssetManager, menjadi hal yang Anda hanya ingin satu, mungkin juga singleton. Asalkan Anda memahami jebakan, dan kode khusus untuk menghindarinya (anggap bahwa singleton akan diakses secara bersamaan dari beberapa utas dan menusuk diri sendiri dengan garpu setiap kali Anda melakukan sesuatu yang perlu diblokir), lalu benturkan diri.


1

Saya pikir Jason D benar - inilah cara saya menanganinya:

Game memiliki instance dari AssetManager, objek dari mana Anda bisa mendapatkan aset apa pun dengan nama.

Dalam permainan:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

Di ScreenManager:

screen = new Screen();
screen.assetManager = assetManager;

Di Layar:

myAsset = assetManager.getBitmp("lava.png");

Sekarang semua layar memiliki akses ke semua aset yang mereka butuhkan. Ini tidak lebih rumit atau gila daripada menggunakan global atau Singletons, dan Anda memiliki opsi untuk menjalankan 2 instance Game dalam aplikasi yang sama tanpa bentrokan. Saya pernah harus membuat game yang terdiri dari 8 mini-game, semua berbagi kelas dasar / kerangka kerja yang sama. Saya harus memperbaiki semua global / lajang saya untuk menggunakan gaya passing referensi ini, dan saya tidak pernah melihat ke belakang. Satu-satunya hal yang seharusnya menjadi global adalah hal-hal yang hanya dapat secara fisik ada satu kali, seperti audio, jaringan, i / o dll.


0

Anda dapat menggunakan pola Pabrik untuk menggantikan Singleton . Kemudian kelas pabrik memiliki kendali atas berapa banyak instance yang dapat Anda buat, yang nantinya dapat Anda ubah dengan mudah ketika Anda membutuhkan lebih dari satu AssetManager. Sebagaimana dinyatakan dalam artikel ini :

Ini memberi Anda semua fleksibilitas Singleton, dengan tempat yang dekat dengan banyak masalah.


Kemungkinan lain, yang agak terbatas, adalah membuat kelas statis (yang menurut saya tidak layak untuk AssetManager dan hanya mungkin dalam bahasa yang memiliki kelas statis sama sekali). Tetapi itu hanya akan berhasil jika Anda tidak membutuhkan warisan / polimorfisme. Ini solusi yang sangat tidak fleksibel:

metode statis fleksibel seperti granit. Setiap kali Anda menggunakannya, Anda menuang bagian dari program Anda dengan beton. Pastikan kaki Anda tidak macet di sana saat Anda melihatnya mengeras. Suatu hari Anda akan kagum bahwa, ya ampun, Anda benar-benar membutuhkan implementasi lain dari kelas PrintSpooler dang itu, dan itu seharusnya merupakan antarmuka, pabrik, dan satu set kelas implementasi. Doh!

Ini tentang metode statis, tetapi juga bisa diterapkan ke kelas statis.


Apa yang Anda maksud dengan "membuat kelas statis"? C ++ tidak memiliki kelas statis. Apakah maksud Anda hanya memiliki metode statis? Mengapa repot-repot memiliki kelas alih-alih namespace?

1
@ Jo: Yah, pertanyaannya tidak fokus pada C ++ seperti yang saya mengerti. Dalam C # atau Java Anda dapat membuat kelas statis dan saya mengacu pada mereka. Juga, seperti yang saya katakan, kelas statis bukanlah solusi yang optimal sebagian besar waktu, tetapi dalam kasus yang jarang dapat bekerja seperti global.
Michael Klement
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.