Variabel global yang dimuliakan - menjadi kelas global yang dimuliakan. Ada yang mengatakan melanggar desain berorientasi objek.
Berikan saya skenario, selain penebang tua yang baik di mana masuk akal untuk menggunakan singleton.
Variabel global yang dimuliakan - menjadi kelas global yang dimuliakan. Ada yang mengatakan melanggar desain berorientasi objek.
Berikan saya skenario, selain penebang tua yang baik di mana masuk akal untuk menggunakan singleton.
Jawaban:
Dalam pencarian saya akan kebenaran, saya menemukan bahwa sebenarnya ada sangat sedikit alasan "dapat diterima" untuk menggunakan Singleton.
Salah satu alasan yang cenderung muncul berulang-ulang di internet adalah karena kelas "logging" (yang Anda sebutkan). Dalam hal ini, Singleton dapat digunakan sebagai pengganti satu instance kelas karena kelas logging biasanya perlu digunakan berulang-ulang untuk mual oleh setiap kelas dalam suatu proyek. Jika setiap kelas menggunakan kelas logging ini, injeksi ketergantungan menjadi rumit.
Logging adalah contoh spesifik dari Singleton yang "dapat diterima" karena itu tidak mempengaruhi eksekusi kode Anda. Nonaktifkan logging, eksekusi kode tetap sama. Aktifkan, sama saja. Misko memasukkannya dengan cara berikut dalam Root Cause of Singletons , "Informasi di sini mengalir satu arah: Dari aplikasi Anda ke dalam logger. Meskipun logger adalah keadaan global, karena tidak ada informasi mengalir dari logger ke dalam aplikasi Anda, logger dapat diterima."
Saya yakin ada alasan sah lainnya juga. Alex Miller, dalam " Patterns I Hate ", pembicaraan tentang pencari layanan dan UI sisi klien juga kemungkinan pilihan yang "dapat diterima".
Baca lebih lanjut di Singleton, aku mencintaimu, tetapi kau menjatuhkanku.
Kandidat Singleton harus memenuhi tiga persyaratan:
Jika Singleton yang Anda usulkan hanya memiliki satu atau dua persyaratan ini, pendesainan ulang hampir selalu merupakan pilihan yang tepat.
Misalnya, spooler printer tidak mungkin dipanggil dari lebih dari satu tempat (menu Print), sehingga Anda dapat menggunakan mutex untuk menyelesaikan masalah akses bersamaan.
Logger sederhana adalah contoh yang paling jelas dari Singleton yang mungkin valid, tetapi ini dapat berubah dengan skema logging yang lebih kompleks.
Membaca file konfigurasi yang seharusnya hanya dibaca pada waktu startup dan merangkumnya dalam Singleton.
Properties.Settings.Default
di .NET.
Anda menggunakan singleton ketika Anda perlu mengelola sumber daya bersama. Misalnya spooler printer. Aplikasi Anda hanya boleh memiliki satu instance spooler untuk menghindari permintaan yang saling bertentangan untuk sumber daya yang sama.
Atau koneksi database atau file manager dll.
Baca hanya orang lajang yang menyimpan beberapa keadaan global (bahasa pengguna, bantuan filepath, jalur aplikasi) masuk akal. Berhati-hatilah menggunakan lajang untuk mengendalikan logika bisnis - tunggal hampir selalu berakhir dengan banyak
Mengelola koneksi (atau kumpulan koneksi) ke database.
Saya akan menggunakannya juga untuk mengambil dan menyimpan informasi pada file konfigurasi eksternal.
Salah satu cara Anda menggunakan singleton adalah untuk meliput contoh di mana harus ada "broker" tunggal yang mengontrol akses ke sumber daya. Lajang baik dalam logger karena mereka broker akses ke, katakanlah, file, yang hanya dapat ditulis secara eksklusif. Untuk sesuatu seperti pencatatan, mereka menyediakan cara untuk mengabstraksi penulisan ke sesuatu seperti file log - Anda bisa membungkus mekanisme caching ke singleton Anda, dll ...
Juga pikirkan situasi di mana Anda memiliki aplikasi dengan banyak jendela / utas / etc, tetapi yang membutuhkan satu titik komunikasi. Saya pernah menggunakan satu untuk mengontrol pekerjaan yang saya ingin aplikasi saya luncurkan. Singleton bertanggung jawab untuk membuat serial pekerjaan dan menampilkan status mereka ke bagian lain dari program yang tertarik. Dalam skenario semacam ini, Anda dapat melihat singleton sebagai semacam "server" yang berjalan di dalam aplikasi Anda ... HTH
Satu singleton harus digunakan ketika mengelola akses ke sumber daya yang dibagikan oleh seluruh aplikasi, dan akan merusak jika berpotensi memiliki banyak instance dari kelas yang sama. Memastikan bahwa akses ke sumber daya bersama thread aman adalah salah satu contoh yang sangat baik di mana pola semacam ini sangat penting.
Saat menggunakan lajang, Anda harus memastikan bahwa Anda tidak sengaja menyembunyikan dependensi. Idealnya, lajang (seperti kebanyakan variabel statis dalam suatu aplikasi) diatur selama eksekusi kode inisialisasi Anda untuk aplikasi (static void Main () untuk C # executable, static void main () untuk executable java) dan kemudian diteruskan ke semua kelas lain yang dipakai yang membutuhkannya. Ini membantu Anda mempertahankan testability.
Contoh praktis dari singleton dapat ditemukan di Test :: Builder , kelas yang mendukung hampir semua modul pengujian Perl modern. Test :: Builder singleton menyimpan dan memperantarai keadaan dan sejarah proses pengujian (hasil tes historis, menghitung jumlah tes yang dijalankan) serta hal-hal seperti ke mana hasil tes akan berjalan. Ini semua diperlukan untuk mengoordinasikan beberapa modul pengujian, yang ditulis oleh penulis yang berbeda, untuk bekerja bersama dalam satu skrip pengujian.
Sejarah Test :: singleton Builder bersifat mendidik. Memanggil new()
selalu memberi Anda objek yang sama. Pertama, semua data disimpan sebagai variabel kelas dengan tidak ada di objek itu sendiri. Ini bekerja sampai saya ingin menguji Test :: Builder dengan sendirinya. Kemudian saya membutuhkan dua objek Test :: Builder, satu setup sebagai dummy, untuk menangkap dan menguji perilaku dan outputnya, dan satu untuk menjadi objek tes yang sebenarnya. Pada titik itu Test :: Builder di refactored menjadi objek nyata. Objek tunggal disimpan sebagai data kelas, dan new()
akan selalu mengembalikannya. create()
telah ditambahkan untuk membuat objek segar dan mengaktifkan pengujian.
Saat ini, pengguna ingin mengubah beberapa perilaku Test :: Builder dalam modul mereka sendiri, tetapi membiarkan yang lain sendirian, sementara riwayat tes tetap sama di semua modul pengujian. Apa yang terjadi sekarang adalah Uji monolitik :: objek Builder sedang dipecah menjadi potongan-potongan kecil (sejarah, output, format ...) dengan contoh Test :: Builder mengumpulkan mereka bersama-sama. Sekarang Uji :: Builder tidak lagi harus tunggal. Komponen-komponennya, seperti sejarah, bisa jadi. Ini mendorong kebutuhan yang tidak fleksibel dari seorang singleton ke tingkat yang lebih rendah. Ini memberi lebih banyak fleksibilitas kepada pengguna untuk mencampur dan mencocokkan bagian. Objek singleton yang lebih kecil sekarang hanya dapat menyimpan data, dengan objeknya yang berisi memutuskan bagaimana menggunakannya. Bahkan memungkinkan kelas non-Test :: Builder untuk bermain bersama dengan menggunakan Test :: Builder history dan output singletons.
Tampaknya ada dorongan dan tarik antara koordinasi data dan fleksibilitas perilaku yang dapat dikurangi dengan menempatkan singleton sekitar hanya data yang dibagikan dengan jumlah perilaku sekecil mungkin untuk memastikan integritas data.
Ketika Anda memuat objek Properties konfigurasi, baik dari database atau file, itu membantu untuk memilikinya sebagai singleton; tidak ada alasan untuk terus membaca kembali data statis yang tidak akan berubah saat server sedang berjalan.
Anda dapat menggunakan Singleton saat menerapkan pola Negara (dengan cara yang ditunjukkan dalam buku GoF). Ini karena kelas Negara konkret tidak memiliki negara mereka sendiri, dan melakukan tindakan mereka dalam konteks kelas konteks.
Anda juga dapat membuat Pabrik Abstrak menjadi singleton.
setState()
bertanggung jawab untuk memutuskan kebijakan pembuatan negara. Ini membantu jika bahasa pemrograman Anda mendukung templat atau generik. Alih-alih Singleton, Anda bisa menggunakan pola Monostate , di mana instantiating objek negara akhirnya menggunakan kembali objek keadaan global / statis yang sama. Sintaks untuk mengubah status bisa tetap tidak berubah, karena pengguna Anda tidak perlu sadar bahwa keadaan yang dipakai adalah Monostate.
Sumber daya bersama. Terutama di PHP, kelas basis data, kelas templat, dan kelas depot variabel global. Semua harus dibagikan oleh semua modul / kelas yang digunakan di seluruh kode.
Ini adalah penggunaan objek yang benar -> kelas template berisi templat halaman yang sedang dibangun, dan itu akan dibentuk, ditambahkan, diubah oleh modul yang ditambahkan ke output halaman. Itu harus disimpan sebagai satu contoh sehingga ini bisa terjadi, dan hal yang sama berlaku untuk database. Dengan singleton basis data bersama, semua kelas modul bisa mendapatkan akses ke pertanyaan dan mendapatkannya tanpa harus memutarnya kembali.
Depot variabel global tunggal memberikan Anda depot variabel global, andal, dan mudah digunakan. Ini merapikan kode Anda banyak. Bayangkan memiliki semua nilai konfigurasi dalam array dalam singleton seperti:
$gb->config['hostname']
atau memiliki semua nilai bahasa dalam array seperti:
$gb->lang['ENTER_USER']
Di akhir menjalankan kode untuk halaman, Anda mendapatkan, katakanlah, sekarang sudah matang:
$template
Singleton, $gb
singleton yang memiliki larik bahasa untuk menggantikannya, dan semua output dimuat dan siap. Anda cukup menggantinya ke dalam kunci yang sekarang hadir dalam nilai halaman objek templat matang, dan kemudian menyajikannya kepada pengguna.
Keuntungan besar dari ini adalah Anda dapat melakukan SETIAP pengolahan pasca Anda suka pada apa pun. Anda dapat menyalurkan semua nilai bahasa ke google translate, atau layanan terjemahan lain dan mendapatkannya kembali, dan menggantinya ke tempat mereka, diterjemahkan, misalnya. atau, Anda dapat mengganti struktur halaman, atau, string konten, seperti yang Anda inginkan.
Akan sangat pragmatis untuk mengkonfigurasi masalah infrastruktur tertentu sebagai lajang atau variabel global. Contoh favorit saya tentang ini adalah kerangka Ketergantungan Injeksi yang menggunakan lajang untuk bertindak sebagai titik koneksi ke kerangka kerja.
Dalam hal ini Anda mengambil ketergantungan pada infrastruktur untuk menyederhanakan menggunakan perpustakaan dan menghindari kompleksitas yang tidak dibutuhkan.
Saya menggunakannya untuk objek mengenkapsulasi parameter baris perintah ketika berhadapan dengan modul pluggable. Program utama tidak tahu apa parameter baris perintah untuk modul yang dimuat (dan bahkan tidak selalu tahu modul apa yang dimuat). misalnya, beban utama A, yang tidak memerlukan parameter apa pun (jadi mengapa harus mengambil pointer / referensi tambahan / apa pun, saya tidak yakin - terlihat seperti polusi), lalu memuat modul X, Y, dan Z. Dua dari ini, katakanlah X dan Z, perlu (atau menerima) parameter, sehingga mereka memanggil kembali ke singleton baris perintah untuk memberi tahu itu parameter apa yang harus diterima, dan pada saat runtime mereka memanggil kembali untuk mencari tahu apakah pengguna benar-benar telah menentukan apa saja dari mereka.
Dalam banyak hal, satu singleton untuk menangani parameter CGI akan bekerja sama jika Anda hanya menggunakan satu proses per kueri (metode mod_ * lainnya tidak melakukan ini, jadi akan buruk di sana - sehingga argumen yang mengatakan Anda tidak boleh t gunakan lajang di dunia mod_cgi jika Anda port ke mod_perl atau dunia apa pun).
Contoh dengan kode, mungkin.
Di sini, ConcreteRegistry adalah singleton dalam permainan poker yang memungkinkan perilaku hingga pohon paket mengakses beberapa, antarmuka inti permainan (yaitu, fasad untuk model, tampilan, pengontrol, lingkungan, dll.):
http://www.edmundkirwan.com/servlet/fractal/cs1/frac-cs40.html
Ed.
1 - Sebuah komentar pada jawaban pertama:
Saya tidak setuju dengan kelas Logger statis. ini bisa praktis untuk implementasi, tetapi tidak bisa diganti untuk pengujian unit. Kelas statis tidak dapat diganti dengan tes ganda. Jika Anda tidak menguji unit, Anda tidak akan melihat masalah di sini.
2 - Saya mencoba untuk tidak membuat singleton dengan tangan. Saya hanya membuat objek sederhana dengan konstruktor yang memungkinkan saya untuk menyuntikkan kolaborator ke objek. Jika saya membutuhkan singleton, saya akan menggunakan framework inyection ketergantungan (Spring.NET, Unity for .NET, Spring for Java), atau yang lainnya.
ILogger logger = Logger.SingleInstance();
mana metode ini statis dan mengembalikan contoh ILogger yang disimpan secara statis. Anda menggunakan contoh "kerangka kerja injeksi ketergantungan". Hampir semua wadah DI adalah lajang; konfigurasi mereka didefinisikan secara statis dan pada akhirnya diakses dari / disimpan dalam antarmuka penyedia layanan tunggal.