Saya telah membaca berbagai pendapat tentang pola singleton. Beberapa berpendapat bahwa itu harus dihindari dengan cara apa pun dan yang lain itu dapat berguna dalam situasi tertentu.
Satu situasi di mana saya menggunakan lajang adalah ketika saya membutuhkan sebuah pabrik (katakanlah objek f dari tipe F) untuk membuat objek dari kelas tertentu A. Pabrik dibuat sekali menggunakan beberapa parameter konfigurasi dan kemudian digunakan setiap kali objek dari tipe A dipakai. Jadi setiap bagian dari kode yang ingin instantiate A mengambil singleton f dan membuat instance baru, misalnya
F& f = F::instance();
boost::shared_ptr<A> a = f.createA();
Jadi secara umum skenario saya adalah itu
- Saya hanya perlu satu instance kelas baik untuk alasan optimasi (saya tidak perlu beberapa objek pabrik) atau untuk berbagi keadaan umum (mis. Pabrik tahu berapa banyak instance A yang masih dapat dibuat)
- Saya perlu cara untuk memiliki akses ke instance F ini di tempat kode yang berbeda.
Saya tidak tertarik pada diskusi apakah pola ini baik atau buruk, tetapi dengan asumsi saya ingin menghindari penggunaan singleton, pola apa lagi yang bisa saya gunakan?
Gagasan yang saya miliki adalah (1) untuk mendapatkan objek pabrik dari registri atau (2) untuk membuat pabrik di beberapa titik selama program dimulai dan kemudian melewati pabrik sebagai parameter.
Dalam solusi (1), registri itu sendiri adalah singleton, jadi saya baru saja menggeser masalah tidak menggunakan singleton dari pabrik ke registri.
Dalam kasus (2) saya memerlukan beberapa sumber awal (objek) dari mana objek pabrik berasal sehingga saya takut bahwa saya akan kembali ke singleton lain (objek yang menyediakan contoh pabrik saya). Dengan mengikuti kembali rantai lajang ini saya mungkin dapat mengurangi masalah menjadi satu lajang (seluruh aplikasi) dimana semua lajang lainnya dikelola secara langsung atau tidak langsung.
Apakah opsi terakhir ini (menggunakan satu singleton awal yang menciptakan semua objek unik lainnya dan menyuntikkan semua lajang lainnya di tempat yang tepat) menjadi solusi yang dapat diterima? Apakah ini solusi yang disarankan secara implisit ketika seseorang menyarankan untuk tidak menggunakan lajang, atau apa solusi lain, misalnya dalam contoh yang diilustrasikan di atas?
EDIT
Karena saya pikir pertanyaan saya telah disalahpahami oleh beberapa orang, berikut adalah beberapa informasi. Seperti yang dijelaskan misalnya di sini , kata singleton dapat menunjukkan (a) kelas dengan objek instance tunggal dan (b) pola desain yang digunakan untuk membuat dan mengakses objek tersebut.
Untuk memperjelas sesuatu, mari kita gunakan istilah objek unik untuk (a) dan pola tunggal untuk (b). Jadi, saya tahu apa pola singleton dan injeksi ketergantungan (BTW, akhir-akhir ini saya banyak menggunakan DI untuk menghapus instance dari pola singleton dari beberapa kode yang saya kerjakan).
Maksud saya adalah bahwa kecuali seluruh grafik objek adalah instantiated dari satu objek yang hidup pada tumpukan metode utama, akan selalu ada kebutuhan untuk mengakses beberapa objek unik melalui pola singleton.
Pertanyaan saya adalah apakah memiliki pembuatan grafik lengkap dan pengkabelan bergantung pada metode utama (misalnya melalui beberapa kerangka DI yang kuat yang tidak menggunakan pola itu sendiri) adalah satu-satunya solusi bebas pola tunggal .
Jawaban:
Opsi kedua Anda adalah cara yang baik untuk dilakukan - ini adalah semacam suntikan ketergantungan, yang merupakan pola yang digunakan untuk berbagi status di seluruh program Anda saat Anda ingin menghindari lajang dan variabel global.
Anda tidak dapat menyiasati kenyataan bahwa sesuatu harus membuat pabrik Anda. Jika sesuatu terjadi pada aplikasi, maka jadilah itu. Poin pentingnya adalah bahwa pabrik Anda seharusnya tidak peduli benda apa yang membuatnya, dan benda-benda yang menerima pabrik tidak boleh bergantung pada dari mana pabrik itu berasal. Jangan minta benda Anda mendapatkan pointer ke singleton aplikasi dan memintanya untuk pabrik; minta aplikasi Anda membuat pabrik dan memberikannya kepada benda-benda yang membutuhkannya.
sumber
Tujuan seorang lajang adalah untuk menegakkan bahwa hanya satu contoh yang pernah ada dalam dunia tertentu. Ini berarti bahwa singleton berguna jika Anda memiliki alasan kuat untuk menegakkan perilaku singleton; dalam praktiknya, ini jarang terjadi, dan multi-pemrosesan dan multi-threading jelas mengaburkan makna 'unik' - apakah ini satu contoh per mesin, per proses, per utas, per permintaan? Dan apakah implementasi tunggal Anda merawat kondisi ras?
Alih-alih singleton, saya lebih suka menggunakan salah satu dari:
Alasannya adalah bahwa singleton adalah negara global yang menyamar, yang berarti itu memperkenalkan tingkat tinggi dari penggabungan seluruh aplikasi - bagian mana pun dari kode Anda dapat mengambil instance singleton dari mana saja dengan upaya minimal. Jika Anda menggunakan benda-benda lokal atau memberikan contoh, Anda memiliki kontrol lebih besar, dan Anda dapat menjaga cakupan Anda kecil dan dependensi Anda sempit.
sumber
Kebanyakan orang (termasuk Anda) benar-benar salah memahami apa sebenarnya pola Singleton. Pola Singleton hanya berarti bahwa ada satu instance dari sebuah kelas dan ada beberapa mekanisme untuk kode di seluruh aplikasi untuk mendapatkan referensi ke instance itu.
Dalam buku GoF, metode pabrik statis yang mengembalikan referensi ke bidang statis hanyalah contoh bagaimana mekanisme itu mungkin terlihat, dan metode yang memiliki kelemahan parah. Sayangnya, semua orang dan anjing mereka menggunakan mekanisme itu dan mengira itu adalah tentang Singleton.
Alternatif yang Anda kutip sebenarnya juga lajang, hanya dengan mekanisme berbeda untuk mendapatkan referensi. (2) jelas mengakibatkan terlalu banyak melewati, kecuali Anda memerlukan referensi hanya di beberapa tempat di dekat akar tumpukan panggilan. (1) terdengar seperti kerangka kerja injeksi ketergantungan kasar - jadi mengapa tidak menggunakannya?
Keuntungannya adalah bahwa kerangka kerja DI yang ada fleksibel, kuat dan teruji dengan baik. Mereka dapat melakukan lebih dari sekedar mengelola Lajang. Namun, untuk sepenuhnya menggunakan kemampuan mereka, mereka bekerja paling baik jika Anda menyusun aplikasi Anda dengan cara tertentu, yang tidak selalu mungkin: idealnya, ada objek pusat yang diperoleh melalui kerangka kerja DI dan memiliki semua dependensi mereka yang terisi secara transitif dan kemudian dieksekusi.
Sunting: Pada akhirnya, semuanya tergantung pada metode utama. Tapi Anda benar: satu-satunya cara untuk menghindari penggunaan global / statis sepenuhnya adalah dengan mengatur semuanya dari metode utama. Perhatikan bahwa DI paling populer di lingkungan server di mana metode utama adalah buram dan mengatur server dan kode aplikasi pada dasarnya terdiri dari panggilan balik yang dipakai dan dipanggil oleh kode server. Tetapi sebagian besar kerangka kerja DI juga memungkinkan akses langsung ke registri pusat mereka, misalnya Spring's ApplicationContext, untuk "kasus khusus".
Jadi pada dasarnya, hal terbaik yang telah dikemukakan orang sejauh ini adalah kombinasi cerdas dari dua alternatif yang Anda sebutkan.
sumber
Ada beberapa alternatif:
Injeksi Ketergantungan
Setiap objek memiliki dependensi yang diteruskan kepadanya ketika objek itu dibuat. Biasanya kerangka kerja atau sejumlah kecil kelas pabrik bertanggung jawab untuk membuat dan menghubungkan objek
Daftar Layanan
Setiap objek melewati objek registri layanan. Ini menyediakan metode untuk meminta berbagai objek yang berbeda yang menyediakan layanan yang berbeda. Kerangka kerja Android menggunakan pola ini
Manajer Root
Ada satu objek yang merupakan akar dari grafik objek. Dengan mengikuti tautan dari objek ini, objek lain akhirnya dapat ditemukan. Kode cenderung terlihat seperti:
sumber
Singleton::instance
di mana-mana dalam kode klien.Jika kebutuhan Anda dari singleton dapat dirubah menjadi satu fungsi, mengapa tidak menggunakan fungsi pabrik yang sederhana? Fungsi global (mungkin metode statis kelas F dalam contoh Anda) pada dasarnya adalah lajang, dengan keunikan ditegakkan oleh kompiler dan penghubung.
Memang, ini rusak ketika operasi singleton tidak dapat direduksi menjadi panggilan fungsi tunggal, meskipun mungkin satu set kecil fungsi terkait dapat membantu Anda.
Semua yang dikatakan, item 1 dan 2 dalam pertanyaan Anda menjelaskan bahwa Anda benar-benar hanya menginginkan satu dari sesuatu. Tergantung pada definisi Anda tentang pola singleton, Anda sudah menggunakannya atau sangat dekat. Saya tidak berpikir Anda dapat memiliki salah satu dari sesuatu tanpa menjadi tunggal atau setidaknya sangat dekat dengan satu. Itu terlalu dekat dengan makna singleton.
Seperti yang Anda sarankan, pada titik tertentu Anda harus memiliki sesuatu, jadi mungkin masalahnya adalah tidak memiliki satu contoh pun dari sesuatu, tetapi mengambil langkah-langkah untuk mencegah (atau setidaknya mencegah atau meminimalkan) penyalahgunaannya. Memindahkan status keluar dari "singleton" ke dalam parameter sebanyak mungkin adalah awal yang baik. Mempersempit antarmuka ke singleton juga membantu, karena ada lebih sedikit peluang untuk penyalahgunaan seperti itu. Terkadang Anda hanya perlu menyedotnya dan membuat singleton sangat kuat, seperti heap atau filesystem.
sumber
Jika Anda menggunakan bahasa multiparadigma seperti C ++ atau Python, salah satu alternatif untuk kelas singleton adalah seperangkat fungsi / variabel yang dibungkus dalam namespace.
Secara konseptual, file C ++ dengan variabel global gratis, fungsi global gratis, dan variabel statis yang digunakan untuk menyembunyikan informasi, semuanya dibungkus dalam namespace, memberi Anda efek yang hampir sama dengan "kelas" tunggal.
Itu hanya rusak jika Anda ingin warisan. Saya telah melihat banyak lajang yang akan lebih baik dari sini.
sumber
Enkapsulasi lajang
Skenario kasus. Aplikasi Anda Berorientasi Objek, dan membutuhkan 3 atau 4 lajang khusus, bahkan jika Anda memiliki lebih banyak kelas.
Sebelum Contoh (C ++ suka pseudocode):
Salah satu caranya adalah dengan menghapus sebagian lajang (global), dengan merangkum semuanya menjadi lajang yang unik, yang dimiliki sebagai anggota, lajang lainnya.
Ini, cara, alih-alih "beberapa lajang, pendekatan, versus, tidak ada lajang sama sekali, pendekatan", kita memiliki "merangkum semua lajang menjadi satu, pendekatan".
Setelah Contoh (C ++ seperti pseudocode):
Harap perhatikan, bahwa contohnya lebih seperti pseudocode, dan abaikan bug minor, atau kesalahan sintaks, dan pertimbangkan solusi untuk pertanyaan tersebut.
Ada juga hal lain yang perlu dipertimbangkan, karena, bahasa pemrograman yang digunakan, dapat mempengaruhi cara mengimplementasikan implementasi singleton atau non singleton Anda.
Tepuk tangan.
sumber
Persyaratan asli Anda:
Jangan sejajar dengan definisi singleton (dan apa yang lebih Anda rujuk nanti). Dari GoF (versi saya adalah 1995) halaman 127:
Jika Anda hanya membutuhkan satu contoh, itu tidak menghalangi Anda untuk membuat lebih banyak.
Jika Anda ingin contoh tunggal yang dapat diakses secara global, buat contoh tunggal yang dapat diakses secara global . Tidak perlu memiliki pola yang dinamai untuk semua yang Anda lakukan. Dan ya, satu contoh yang dapat diakses secara global biasanya buruk. Memberi mereka nama tidak membuat mereka tidak terlalu buruk .
Untuk menghindari aksesibilitas global, pendekatan umum 'membuat sebuah contoh dan meneruskannya' atau 'membuat pemilik dua benda saling menempel' bekerja dengan baik.
sumber
Bagaimana kalau menggunakan wadah IOC? Dengan beberapa pemikiran Anda dapat berakhir dengan sesuatu di baris:
Anda harus menentukan implementasi mana yang
IFoo
akan digunakan pada startup, tetapi ini adalah konfigurasi nonaktif yang nantinya dapat Anda gantikan dengan mudah. Seumur hidup contoh diselesaikan biasanya dapat dikendalikan oleh wadah IoC.Metode singleton void statis dapat diganti dengan:
Metode pengambil singleton statis dapat diganti dengan:
Contoh relevan dengan .NET, tapi saya yakin sangat mirip dapat dicapai dalam bahasa lain.
sumber