it-swarm-id.com

Jadi lajang itu jahat, lalu apa?

Ada banyak diskusi akhir-akhir ini tentang masalah dengan menggunakan (dan menggunakan secara berlebihan) Singletons. Saya telah menjadi salah satu dari orang-orang itu sebelumnya dalam karier saya juga. Saya dapat melihat apa masalahnya sekarang, namun, masih ada banyak kasus di mana saya tidak dapat melihat alternatif yang bagus - dan tidak banyak diskusi anti-Singleton yang benar-benar menyediakannya.

Ini adalah contoh nyata dari proyek besar terbaru yang saya ikuti:

Aplikasi ini adalah klien yang tebal dengan banyak layar dan komponen terpisah yang menggunakan sejumlah besar data dari keadaan server yang tidak terlalu sering diperbarui. Data ini pada dasarnya di-cache dalam objek "manajer" Singleton - "negara global" yang ditakuti. Idenya adalah untuk memiliki satu tempat ini di aplikasi yang menyimpan data disimpan dan disinkronkan, dan kemudian setiap layar baru yang dibuka hanya dapat meminta sebagian besar dari apa yang mereka butuhkan dari sana, tanpa membuat permintaan berulang untuk berbagai data pendukung dari server. Terus-menerus meminta ke server akan memakan terlalu banyak bandwidth - dan saya sedang berbicara ribuan dolar tagihan internet tambahan per minggu, jadi itu tidak dapat diterima.

Apakah ada pendekatan lain yang bisa sesuai di sini daripada pada dasarnya memiliki objek cache data manager global semacam ini? Objek ini tidak secara resmi harus menjadi "Singleton", tetapi secara konseptual masuk akal untuk menjadi "Singleton". Apa alternatif bersih yang bagus di sini?

570
Bobby Tables

Penting untuk membedakan di sini antara contoh tunggal dan pola desain Singleton .

Mesin virtual tunggal hanyalah sebuah kenyataan. Sebagian besar aplikasi hanya dirancang untuk bekerja dengan satu konfigurasi pada satu waktu, satu UI pada suatu waktu, satu sistem file pada suatu waktu, dan sebagainya. Jika ada banyak status atau data yang harus dipelihara, maka tentu Anda ingin memiliki hanya satu contoh dan tetap hidup selama mungkin.

Pola desain Singleton adalah tipe yang sangat spesifik dari instance tunggal, khususnya yang adalah:

  • Dapat diakses melalui bidang instance global, statis;
  • Dibuat baik pada inisialisasi program atau pada akses pertama;
  • Tidak ada konstruktor publik (tidak dapat membuat instantiate secara langsung);
  • Tidak pernah secara eksplisit dibebaskan (secara implisit dibebaskan pada penghentian program).

Karena pilihan desain khusus inilah maka pola tersebut memperkenalkan beberapa potensi masalah jangka panjang:

  • Ketidakmampuan untuk menggunakan kelas abstrak atau antarmuka;
  • Ketidakmampuan untuk subkelas;
  • Kopling tinggi di seluruh aplikasi (sulit untuk dimodifikasi);
  • Sulit untuk diuji (tidak dapat memalsukan/mengejek dalam unit test);
  • Sulit untuk diparalelkan dalam kasus keadaan yang bisa berubah (membutuhkan penguncian yang luas);
  • dan seterusnya.

Tidak satu pun dari gejala-gejala ini yang benar-benar endemik pada kejadian tunggal, hanya pola Singleton.

Apa yang bisa kamu lakukan? Cukup tidak menggunakan pola Singleton.

Mengutip dari pertanyaan:

Idenya adalah untuk memiliki satu tempat ini di aplikasi yang menyimpan data disimpan dan disinkronkan, dan kemudian setiap layar baru yang dibuka hanya dapat meminta sebagian besar dari apa yang mereka butuhkan dari sana, tanpa membuat permintaan berulang untuk berbagai data pendukung dari server. Terus-menerus meminta ke server akan memakan terlalu banyak bandwidth - dan saya sedang berbicara ribuan dolar tagihan internet tambahan per minggu, jadi itu tidak dapat diterima.

Konsep ini memiliki nama, saat Anda mengisyaratkan tetapi terdengar tidak pasti. Ini disebut cache . Jika Anda ingin menjadi mewah, Anda dapat menyebutnya "cache offline" atau hanya salinan offline data jarak jauh.

Cache tidak harus berupa singleton. Itu mungkin perlu menjadi satu instance jika Anda ingin menghindari mengambil data yang sama untuk beberapa instance cache; tapi itu tidak berarti Anda benar-benar harus memaparkan semuanya kepada semua orang .

Hal pertama yang saya lakukan adalah memisahkan area fungsional yang berbeda dari cache menjadi antarmuka yang terpisah. Misalnya, katakanlah Anda membuat klon YouTube terburuk di dunia berdasarkan Microsoft Access:

 MSAccessCache 
 ▲ 
 | 
 + ----------------- + -------- --------- + 
 | | | 
 IMediaCache IProfileCache IPageCache 
 | | | 
 | | | 
 VideoPage MyAccountPage MostPopularPage 

Di sini Anda memiliki beberapa antarmuka yang menggambarkan jenis data tertentu yang mungkin perlu diakses oleh kelas tertentu - media, profil pengguna, dan statis halaman (seperti halaman depan). Semua itu diimplementasikan oleh satu mega-cache, tetapi Anda mendesain masing-masing kelas untuk menerima antarmuka, jadi mereka tidak peduli seperti apa instance yang mereka miliki. Anda menginisialisasi instance fisik sekali, ketika program Anda mulai, dan kemudian mulai membagikan instance (dilemparkan ke jenis antarmuka tertentu) melalui konstruktor dan properti publik.

Ini disebut Injeksi Ketergantungan , omong-omong; Anda tidak perlu menggunakan Spring atau wadah IoC khusus, asalkan desain kelas umum Anda menerima dependensinya dari pemanggil alih-alih instantiating mereka sendiri atau merujuk negara global .

Mengapa Anda harus menggunakan desain berbasis antarmuka? Tiga alasan:

  1. Itu membuat kode lebih mudah dibaca; Anda dapat dengan jelas memahami dari antarmuka persis data apa yang bergantung pada kelas dependen.

  2. Jika dan ketika Anda menyadari bahwa Microsoft Access bukan pilihan terbaik untuk back-end data, Anda dapat menggantinya dengan sesuatu yang lebih baik - katakanlah SQL Server.

  3. Jika dan ketika Anda menyadari bahwa SQL Server bukan pilihan terbaik untuk media khusus , Anda dapat memecah implementasi Anda tanpa mempengaruhi bagian lain dari media. sistem. Di situlah kekuatan abstraksi yang sesungguhnya muncul.

Jika Anda ingin mengambil satu langkah lebih jauh maka Anda dapat menggunakan wadah IoC (kerangka kerja DI) seperti Spring (Java) atau Unity (.NET). Hampir setiap kerangka DI akan melakukan manajemen seumur hidupnya sendiri dan secara khusus memungkinkan Anda untuk mendefinisikan layanan tertentu sebagai satu contoh (sering menyebutnya "singleton", tetapi itu hanya untuk keakraban). Pada dasarnya kerangka kerja ini menyelamatkan Anda sebagian besar dari pekerjaan monyet untuk secara manual menyebarkan instance, tetapi mereka tidak sepenuhnya diperlukan. Anda tidak memerlukan alat khusus untuk menerapkan desain ini.

Demi kelengkapan, saya harus menunjukkan bahwa desain di atas juga tidak ideal. Ketika Anda berurusan dengan cache (seperti Anda), Anda seharusnya benar-benar memiliki layer yang sepenuhnya terpisah . Dengan kata lain, desain seperti ini:

 + - IMediaRepository 
 | 
 Cache (Generic) --------------- + - IProfileRepository 
 ▲ | 
 | + - IPageRepository 
 + ----------------- + ----------------- + 
 | | | 
 IMediaCache IProfileCache IPageCache 
 | | | 
 | | | 
 VideoPage MyAccountPage MostPopularPage 

Keuntungannya adalah Anda bahkan tidak perlu memecah instance Cache Anda jika Anda memutuskan untuk melakukan refactor; Anda dapat mengubah cara Media disimpan hanya dengan memberinya implementasi alternatif IMediaRepository. Jika Anda berpikir tentang bagaimana ini cocok bersama, Anda akan melihat bahwa itu hanya pernah membuat satu instance fisik dari cache, jadi Anda tidak perlu mengambil data yang sama dua kali.

Tidak satu pun dari ini yang mengatakan bahwa setiap bagian dari perangkat lunak di dunia perlu dirancang sesuai dengan standar kohesi tinggi dan kopling longgar ini; itu tergantung pada ukuran dan ruang lingkup proyek, tim Anda, anggaran Anda, tenggat waktu, dll. Tetapi jika Anda bertanya apa desain terbaik (untuk digunakan sebagai pengganti singleton), maka ini dia.

P.S. Seperti yang telah dinyatakan orang lain, mungkin bukan ide terbaik bagi kelas-kelas dependen untuk menyadari bahwa mereka menggunakan cache - itu adalah detail implementasi yang seharusnya tidak pernah mereka pedulikan. Yang sedang berkata, keseluruhan arsitektur masih akan terlihat sangat mirip dengan apa yang digambarkan di atas, Anda hanya tidak akan merujuk ke antarmuka individu sebagai Tembolok . Alih-alih, Anda akan menamainya Layanan atau yang serupa.

826
Aaronaught

Dalam kasus yang Anda berikan, sepertinya penggunaan Singleton bukanlah masalahnya, tetapi gejala dari suatu masalah - masalah arsitektur yang lebih besar.

Mengapa layar meminta objek cache untuk data? Caching harus transparan kepada klien. Seharusnya ada abstraksi yang sesuai untuk menyediakan data, dan implementasi abstraksi itu mungkin menggunakan caching.

Masalahnya adalah bahwa ketergantungan antar bagian sistem tidak diatur dengan benar, dan ini mungkin sistemik.

Mengapa layar harus memiliki pengetahuan tentang dari mana mereka mendapatkan data mereka? Mengapa layar tidak dilengkapi dengan objek yang dapat memenuhi permintaan data (di belakangnya ada cache yang disembunyikan)? Seringkali tanggung jawab untuk membuat layar tidak terpusat, sehingga tidak ada gunanya menyuntikkan dependensi.

Sekali lagi, kami melihat masalah arsitektur dan desain berskala besar.

Juga, sangat penting untuk memahami bahwa seumur hidup dari suatu objek dapat sepenuhnya dipisahkan dari bagaimana objek tersebut ditemukan untuk menggunakan.

Tembolok harus hidup sepanjang masa pakai aplikasi (agar berguna), sehingga masa pakai objek adalah masa pakai Singleton.

Tetapi masalah dengan Singleton (paling tidak implementasi umum Singleton sebagai kelas/properti statis), adalah bagaimana kelas lain yang menggunakannya pergi untuk menemukannya.

Dengan implementasi Singleton yang statis, konvensi ini hanya untuk menggunakannya di mana saja diperlukan. Tapi itu benar-benar menyembunyikan ketergantungan dan pasangan erat dua kelas.

Jika kami memberikan ketergantungan pada kelas, ketergantungan itu eksplisit dan semua kelas konsumsi perlu memiliki pengetahuan tentang kontrak yang tersedia untuk digunakan.

48
quentin-starin

Saya menulis seluruh bab hanya pada pertanyaan ini. Sebagian besar dalam konteks permainan, tetapi sebagian besar harus diterapkan di luar permainan.

tl; dr:

Pola Geng Empat Singleton melakukan dua hal: memberi Anda akses mudah ke objek dari mana saja, dan memastikan bahwa hanya satu instance yang dapat dibuat. 99% dari waktu, yang Anda pedulikan adalah bagian pertama dari itu, dan pengangkutan sepanjang paruh kedua untuk mendapatkannya menambah batasan yang tidak perlu.

Tidak hanya itu, tetapi ada solusi yang lebih baik untuk memberikan akses yang nyaman. Membuat objek global adalah opsi nuklir untuk menyelesaikannya, dan membuatnya mudah untuk menghancurkan enkapsulasi Anda. Segala sesuatu yang buruk tentang global berlaku sepenuhnya untuk para lajang.

Jika Anda menggunakannya hanya karena Anda memiliki banyak tempat dalam kode yang perlu menyentuh objek yang sama, cobalah untuk menemukan cara yang lebih baik untuk memberikannya ke just objek-objek itu tanpa mengeksposnya ke seluruh basis kode. Solusi lain:

  • Selesaikan seluruhnya. Saya telah melihat banyak kelas singleton yang tidak memiliki status apa pun dan hanya merupakan kantong fungsi pembantu. Itu tidak membutuhkan contoh sama sekali. Buat saja mereka fungsi statis, atau pindahkan ke salah satu kelas yang diambil fungsi sebagai argumen. Anda tidak memerlukan kelas Math khusus jika Anda hanya bisa melakukan 123.Abs().

  • Berikan sekitar. Solusi sederhana jika suatu metode membutuhkan beberapa objek lain adalah dengan hanya meneruskannya. Tidak ada yang salah dengan melewatkan beberapa objek di sekitarnya.

  • Letakkan di kelas dasar. Jika Anda memiliki banyak kelas yang semuanya membutuhkan akses ke beberapa objek khusus, dan mereka berbagi kelas dasar, Anda dapat jadikan objek itu anggota di pangkalan. Ketika Anda membangunnya, masukkan objek. Sekarang semua benda yang diturunkan bisa mendapatkannya saat mereka membutuhkannya. Jika Anda membuatnya terlindungi, Anda memastikan objek masih tetap dienkapsulasi.

45
munificent

Bukan negara global yang menjadi masalahnya.

Sungguh Anda hanya perlu khawatir tentang global mutable state. Keadaan konstan tidak terpengaruh oleh pengaruh samping dan dengan demikian kurang dari masalah.

Perhatian utama dengan singleton adalah bahwa itu menambah kopling dan dengan demikian membuat hal-hal seperti pengujian sulit (er). Anda dapat mengurangi sambungan dengan mendapatkan singleton dari sumber lain (mis. Pabrik). Ini akan memungkinkan Anda memisahkan kode dari contoh tertentu (meskipun Anda menjadi lebih terhubung ke pabrik (tetapi setidaknya pabrik dapat memiliki implementasi alternatif untuk fase yang berbeda)).

Dalam situasi Anda, saya pikir Anda bisa lolos begitu saja selama singleton Anda benar-benar mengimplementasikan antarmuka (sehingga alternatif dapat digunakan dalam situasi lain).

Tetapi penarikan utama lain dengan lajang adalah bahwa begitu mereka di tempat menghapusnya dari kode dan menggantinya dengan sesuatu yang lain menjadi tugas yang sangat sulit (ada kopling lagi).

// Example from 5 minutes (con't be too critical)
class ServerFactory
{
    public:
        // By default return a RealServer
        ServerInterface& getServer();

        // Set a non default server:
        void setServer(ServerInterface& server);
};

class ServerInterface { /* define Interface */ };

class RealServer: public ServerInterface {}; // This is a singleton (potentially)

class TestServer: public ServerInterface {}; // This need not be.
21
Martin York

Lalu apa? Karena tidak ada yang mengatakannya: Toolbox. Itu jika Anda ingin variabel global.

Penyalahgunaan Singleton dapat dihindari dengan melihat masalah dari sudut yang berbeda. Misalkan suatu aplikasi hanya membutuhkan satu instance kelas dan aplikasi mengkonfigurasi kelas itu pada saat startup: Mengapa kelas itu sendiri harus bertanggung jawab untuk menjadi seorang lajang? Tampaknya cukup logis bagi aplikasi untuk mengambil tanggung jawab ini, karena aplikasi tersebut memerlukan perilaku semacam ini. Aplikasi, bukan komponen, harus menjadi singleton. Aplikasi kemudian membuat turunan dari komponen yang tersedia untuk setiap kode khusus aplikasi untuk digunakan. Ketika suatu aplikasi menggunakan beberapa komponen seperti itu, ia dapat menggabungkannya ke dalam apa yang kita sebut kotak alat.

Sederhananya, kotak alat aplikasi adalah tunggal yang bertanggung jawab untuk mengkonfigurasi sendiri atau untuk mengizinkan mekanisme startup aplikasi untuk mengkonfigurasinya ...

public class Toolbox {
     private static Toolbox _instance; 

     public static Toolbox Instance {
         get {
             if (_instance == null) {
                 _instance = new Toolbox(); 
             }
             return _instance; 
         }
     }

     protected Toolbox() {
         Initialize(); 
     }

     protected void Initialize() {
         // Your code here
     }

     private MyComponent _myComponent; 

     public MyComponent MyComponent() {
         get {
             return _myComponent(); 
         }
     }
     ... 

     // Optional: standard extension allowing
     // runtime registration of global objects. 
     private Map components; 

     public Object GetComponent (String componentName) {
         return components.Get(componentName); 
     }

     public void RegisterComponent(String componentName, Object component) 
     {
         components.Put(componentName, component); 
     }

     public void DeregisterComponent(String componentName) {
         components.Remove(componentName); 
     }

}

Tapi coba tebak? Itu adalah singleton!

Dan apa itu singleton?

Mungkin di situlah kebingungan dimulai.

Bagi saya, singleton adalah objek yang diberlakukan untuk memiliki satu instance saja dan selalu. Anda dapat mengaksesnya di mana saja, kapan saja, tanpa harus instantiate. Itu sebabnya ini sangat erat hubungannya dengan static . Sebagai perbandingan, static pada dasarnya adalah hal yang sama, kecuali itu bukan contoh. Kita tidak perlu membuat instance, kita bahkan tidak bisa, karena itu dialokasikan secara otomatis. Dan itu bisa dan memang membawa masalah.

Dari pengalaman saya, hanya mengganti static untuk Singleton memecahkan banyak masalah dalam proyek tas kain perca ukuran sedang yang saya jalani. Itu hanya berarti memiliki beberapa penggunaan untuk proyek yang dirancang buruk. Saya pikir ada terlalu banyak diskusi jika pola singleton adalah berguna atau tidak dan saya tidak dapat berdebat jika itu memang buruk) . Tapi masih ada argumen bagus yang mendukung singleton daripada metode statis, secara umum .

Satu-satunya hal yang saya yakin buruk tentang lajang, adalah ketika kita menggunakannya sambil mengabaikan praktik yang baik. Itu memang sesuatu yang tidak mudah untuk dihadapi. Tetapi praktik buruk dapat diterapkan pada pola apa pun. Dan, saya tahu, terlalu umum untuk mengatakan bahwa ... Maksud saya terlalu banyak.

Jangan salah sangka!

Sederhananya, seperti global vars , lajang harus masih = harus dihindari setiap saat . Khususnya karena mereka terlalu dilecehkan. Tetapi vars global tidak dapat selalu dihindari dan kita harus menggunakannya dalam kasus terakhir itu.

Lagi pula, ada banyak saran lain selain Toolbox, dan seperti toolbox, masing-masing memiliki aplikasinya ...

Alternatif lain

  • The artikel terbaik yang baru saja saya baca tentang lajang menyarankan Pencari Layanan sebagai alternatif. Bagi saya itu pada dasarnya adalah " Kotak Alat Statis ", jika Anda mau. Dengan kata lain, buat Penentu Lokasi Layanan menjadi Singleton dan Anda memiliki Kotak Alat. Itu bertentangan dengan saran awalnya untuk menghindari singleton, tentu saja, tapi itu hanya untuk menegakkan masalah singleton adalah bagaimana itu digunakan, bukan pola itu sendiri.

  • Lainnya sarankan Pola Pabrik sebagai alternatif. Itu adalah alternatif pertama yang saya dengar dari seorang kolega dan kami dengan cepat menghilangkannya untuk penggunaan kami sebagai global var. Memang memiliki penggunaannya, tetapi juga lajang.

Kedua alternatif di atas adalah alternatif yang baik. Tetapi itu semua tergantung pada penggunaan Anda.

Sekarang, menyiratkan lajang harus dihindari di semua biaya hanya salah ...

  • Aaronaught Jawabannya menyarankan untuk tidak pernah menggunakan lajang , karena serangkaian alasan . Tapi mereka semua alasan terhadap bagaimana itu digunakan dan disalahgunakan, tidak langsung terhadap pola itu sendiri. Saya setuju dengan semua kekhawatiran tentang poin-poin itu, bagaimana saya tidak bisa? Saya hanya berpikir itu menyesatkan.

Ketidakmampuan (abstrak atau subkelas) memang ada, tapi lalu apa? Bukan untuk itu. Tidak ada ketidakmampuan untuk antarmuka, sejauh saya tah . Tinggi kopling juga bisa ada di sana, tapi itu hanya karena cara umum digunakan. Tidak harus . Faktanya, sambungan itu sendiri tidak ada hubungannya dengan pola tunggal. Yang sedang diklarifikasi, itu juga sudah menghilangkan kesulitan untuk menguji. Adapun kesulitan untuk memparalelkan, itu tergantung pada bahasa dan platform sehingga, sekali lagi, bukan masalah pada polanya.

Contoh-contoh praktis

Saya sering melihat 2 digunakan, baik mendukung dan melawan lajang. Cache web (kasing saya) dan layanan log .

Penebangan, beberapa akan berdebat , adalah contoh tunggal yang sempurna, karena, dan saya kutip:

  • Pemohon membutuhkan objek terkenal untuk mengirim permintaan untuk login. Ini berarti titik akses global.
  • Karena layanan pencatatan merupakan sumber peristiwa tunggal yang dapat didaftarkan banyak pendengar, hanya perlu satu contoh.
  • Meskipun aplikasi yang berbeda dapat masuk ke perangkat output yang berbeda, cara mereka mendaftarkan pendengar mereka selalu sama. Semua penyesuaian dilakukan melalui pendengar. Klien dapat meminta penebangan tanpa mengetahui bagaimana atau di mana teks akan dicatat. Oleh karena itu setiap aplikasi akan menggunakan layanan logging dengan cara yang persis sama.
  • Aplikasi apa pun harus dapat lolos hanya dengan satu instance dari layanan logging.
  • Objek apa pun bisa menjadi pemohon logging, termasuk komponen yang dapat digunakan kembali, sehingga tidak boleh digabungkan ke aplikasi tertentu.

Sementara yang lain akan berpendapat sulit untuk memperluas layanan log begitu Anda akhirnya menyadari bahwa itu seharusnya tidak hanya satu contoh.

Baiklah, saya katakan kedua argumen itu valid. Masalahnya di sini, sekali lagi, bukan pada pola tunggal. Ada pada keputusan arsitektur dan pembobotan jika refactoring adalah risiko yang layak. Ini masalah lebih lanjut ketika, biasanya, refactoring adalah tindakan korektif yang terakhir dibutuhkan.

20
cregox

Masalah utama saya dengan pola desain tunggal adalah sangat sulit untuk menulis unit test yang baik untuk aplikasi Anda.

Setiap komponen yang memiliki ketergantungan pada "manajer" ini melakukannya dengan menanyakan instance singleton-nya. Dan jika Anda ingin menulis unit test untuk komponen seperti itu Anda harus menyuntikkan data ke instance tunggal ini, yang mungkin tidak mudah.

Jika di sisi lain "manajer" Anda disuntikkan ke dalam komponen dependen melalui parameter konstruktor, dan komponen itu tidak tahu tipe konkret manajer, hanya antarmuka, atau kelas dasar abstrak yang diimplementasikan oleh manajer, lalu sebuah unit tes dapat menyediakan implementasi alternatif manajer ketika menguji dependensi.

Jika Anda menggunakan IOC wadah untuk mengkonfigurasi dan membuat instance komponen yang membentuk aplikasi Anda, maka Anda dapat dengan mudah mengkonfigurasi wadah IOC untuk membuat hanya satu instance dari "manajer", memungkinkan Anda untuk mencapai yang sama, hanya satu contoh mengendalikan cache aplikasi global.

Tetapi jika Anda tidak peduli dengan unit test, maka pola desain tunggal bisa baik-baik saja. (Tapi saya tidak akan melakukannya)

5
Pete

Singleton tidak dengan cara yang fundamental buruk, dalam arti bahwa apa pun desain komputasi bisa baik atau buruk. Itu hanya bisa benar (memberikan hasil yang diharapkan) atau tidak. Ini juga bisa bermanfaat atau tidak, jika membuat kode lebih jelas atau lebih efisien.

Satu kasus di mana lajang berguna adalah ketika mereka mewakili suatu entitas yang benar-benar unik. Di sebagian besar lingkungan, database unik, hanya ada satu database. Menyambung ke database itu mungkin rumit karena memerlukan izin khusus, atau melintasi beberapa jenis koneksi. Mengorganisasikan koneksi itu menjadi singleton mungkin masuk akal karena alasan ini saja.

Tetapi Anda juga perlu memastikan bahwa singleton benar-benar singleton, dan bukan variabel global. Ini penting ketika database tunggal dan unik ini benar-benar 4 database, masing-masing untuk produksi, pementasan, pengembangan, dan perlengkapan pengujian. Basis data Singleton akan mencari tahu dari siapa mereka harus terhubung, ambil satu contoh untuk database itu, sambungkan jika diperlukan, dan mengembalikannya ke pemanggil.

Ketika seorang singleton bukan benar-benar seorang singleton (ini adalah saat kebanyakan programmer merasa kesal), itu adalah global yang malas dipakai, tidak ada kesempatan untuk menyuntikkan instance yang benar.

Fitur lain yang bermanfaat dari pola singleton yang dirancang dengan baik adalah sering tidak dapat diamati. Penelepon meminta koneksi. Layanan yang menyediakannya dapat mengembalikan objek yang dikumpulkan, atau jika sedang melakukan tes, ia dapat membuat yang baru untuk setiap penelepon, atau menyediakan objek tiruan.

Penggunaan pola singleton yang mewakili objek aktual sangat dapat diterima. Saya menulis untuk iPhone, dan ada banyak lajang dalam kerangka kerja Cocoa Touch. Aplikasi itu sendiri diwakili oleh singleton kelas UIApplication. Hanya ada satu aplikasi yang Anda, jadi pantas untuk mewakili itu dengan singleton.

Menggunakan singleton sebagai kelas pengelola data tidak masalah selama itu dirancang dengan benar. Jika ini adalah kumpulan properti data, itu tidak lebih baik dari lingkup global. Jika satu set getter dan setter, itu lebih baik, tetapi masih tidak bagus. Jika itu adalah kelas yang benar-benar mengelola semua antarmuka data, termasuk mungkin mengambil data jarak jauh, caching, penyiapan, dan pembongkaran ... Itu bisa sangat berguna.

3
Dan Ray

Lajang hanya proyeksi dari arsitektur berorientasi layanan ke dalam suatu program.

API adalah contoh singleton di tingkat protokol. Anda mengakses Twitter, Google dll melalui apa yang pada dasarnya adalah lajang. Jadi mengapa lajang menjadi buruk dalam suatu program?

Itu tergantung pada bagaimana Anda memikirkan suatu program. Jika Anda menganggap suatu program sebagai masyarakat layanan alih-alih instance yang di-cache secara acak, lajang masuk akal.

Lajang adalah titik akses layanan. Antarmuka publik ke perpustakaan fungsionalitas yang terikat rapat yang mungkin menyembunyikan arsitektur internal yang sangat canggih.

Jadi saya tidak melihat singleton berbeda dari pabrik. Singleton dapat memiliki parameter konstruktor yang dilewatkan. Ini dapat dibuat oleh beberapa konteks yang tahu bagaimana menyelesaikan printer default terhadap semua mekanisme seleksi yang mungkin, misalnya. Untuk pengujian Anda dapat memasukkan tiruan Anda sendiri. Jadi itu bisa sangat fleksibel.

Kuncinya adalah secara internal dalam sebuah program ketika saya menjalankan dan membutuhkan sedikit fungsionalitas, saya dapat mengakses singleton dengan keyakinan penuh bahwa layanan sudah siap dan siap digunakan. Ini adalah kunci ketika ada utas berbeda mulai dalam proses yang harus melalui mesin keadaan untuk dianggap siap.

Biasanya saya akan membungkus kelas XxxService yang membungkus singleton di sekitar kelas Xxx. Singleton tidak ada di kelas Xxx sama sekali, itu dipisahkan menjadi kelas lain, XxxService. Ini karena Xxx dapat memiliki banyak instance, meskipun tidak mungkin, tetapi kami masih ingin memiliki satu instance Xxx yang dapat diakses secara global di setiap sistem. XxxService memberikan pemisahan kekhawatiran yang bagus. Xxx tidak harus memberlakukan kebijakan tunggal, namun kita dapat menggunakan Xxx sebagai singleton ketika kita perlu.

Sesuatu seperti:

//XxxService.h:
/**
 * Provide singleton wrapper for Xxx object. This wrapper
 * can be autogenerated so is not made part of the object.
 */

#include "Xxx/Xxx.h"


class XxxService
{
    public:
    /**
     * Return a Xxx object as a singleton. The double check
     * singleton algorithm is used. A 0 return means there was
     * an error. Developers should use this as the access point to
     * get the Xxx object.
     *
     * <PRE>
     * @@ #include "Xxx/XxxService.h"
     * @@ Xxx* xxx= XxxService::Singleton();
     * <PRE>
     */

     static Xxx*     Singleton();

     private:
         static Mutex  mProtection;
};


//XxxService.cpp:

#include "Xxx/XxxService.h"                   // class implemented
#include "LockGuard.h"     

// CLASS SCOPE
//
Mutex XxxService::mProtection;

Xxx* XxxService::Singleton()
{
    static Xxx* singleton;  // the variable holding the singleton

    // First check to see if the singleton has been created.
    //
    if (singleton == 0)
    {
        // Block all but the first creator.
        //
        LockGuard lock(mProtection);

        // Check again just in case someone had created it
        // while we were blocked.
        //
        if (singleton == 0)
        {
            // Create the singleton Xxx object. It's assigned
            // to a temporary so other accessors don't see
            // the singleton as created before it really is.
            //
            Xxx* inprocess_singleton= new Xxx;

            // Move the singleton to state online so we know that is has
            // been created and it ready for use.
            //
            if (inprocess_singleton->MoveOnline())
            {
                LOG(0, "XxxService:Service: FAIL MoveOnline");
                return 0;
            }

            // Wait until the module says it's in online state.
            //
            if (inprocess_singleton->WaitTil(Module::MODULE_STATE_ONLINE))
            {
                LOG(0, "XxxService:Service: FAIL move to online");
                return 0;
            }

            // The singleton is created successfully so assign it.
            //
            singleton= inprocess_singleton;


        }// still not created
    }// not created

    // Return the created singleton.
    //
    return singleton;

}// Singleton  
3
Todd Hoff

Mintalah setiap layar mengambil Manajer dalam konstruktor mereka.

Ketika Anda memulai aplikasi Anda, Anda membuat satu instance dari manajer dan menyebarkannya.

Ini disebut Inversion of Control, dan memungkinkan Anda untuk menukar controller ketika konfigurasi berubah dan dalam pengujian. Selain itu, Anda dapat menjalankan beberapa contoh aplikasi Anda atau bagian dari aplikasi Anda secara paralel (baik untuk pengujian!). Terakhir manajer Anda akan mati dengan objeknya sendiri (kelas startup).

Jadi susun aplikasi Anda seperti pohon, di mana benda-benda di atas memiliki semua yang digunakan di bawahnya. Jangan menerapkan aplikasi seperti jaring, di mana semua orang mengenal semua orang dan menemukan satu sama lain melalui metode global.

1

IMO, contoh Anda terdengar oke. Saya menyarankan anjak keluar sebagai berikut: objek cache untuk masing-masing (dan di belakang masing-masing) objek data; objek cache dan objek pengakses db memiliki antarmuka yang sama. Ini memberikan kemampuan untuk menukar cache masuk dan keluar dari kode; plus itu memberikan rute ekspansi yang mudah.

Grafis:

DB
|
DB Accessor for OBJ A
| 
Cache for OBJ A
|
OBJ A Client requesting

Accessor dan cache DB dapat mewarisi dari objek yang sama atau tipe bebek menjadi tampak seperti objek yang sama, apa pun. Selama Anda bisa pasang/kompilasi/uji dan masih berfungsi.

Ini memisahkan hal-hal sehingga Anda dapat menambahkan cache baru tanpa harus masuk dan memodifikasi beberapa objek Uber-Cache. YMMV. IANAL. DLL.

1
Paul Nathan

Pertanyaan pertama, apakah Anda menemukan banyak bug dalam aplikasi? mungkin lupa memperbarui cache, atau cache buruk atau sulit diubah? (Saya ingat sebuah aplikasi tidak akan mengubah ukuran kecuali jika Anda mengubah warnanya juga ... Anda dapat mengubah kembali warnanya dan mempertahankan ukurannya).

Apa yang akan Anda lakukan adalah memiliki kelas itu tetapi HAPUS SEMUA ANGGOTA STATIK. Ok ini bukan nessacary tetapi saya merekomendasikannya. Benar-benar Anda hanya menginisialisasi kelas seperti kelas normal dan LULUSkan pointer. Jangan frigen katakan ClassIWant.APtr (). LetMeChange.ANYTHINGATALL () .andhave_no_structure ()

Ini lebih banyak bekerja tetapi sebenarnya, itu kurang membingungkan. Beberapa tempat di mana Anda tidak boleh mengubah hal-hal yang sekarang tidak dapat Anda lakukan karena tidak lagi bersifat global. Semua kelas manajer saya adalah kelas reguler, anggap saja seperti itu.

1
user2528

Agak terlambat ke pesta, tapi lagian.

Singleton adalah alat dalam kotak peralatan, sama seperti yang lainnya. Semoga Anda memiliki lebih banyak di kotak peralatan Anda daripada hanya satu palu.

Pertimbangkan ini:

public void DoSomething()
{
    MySingleton.Instance.Work();
}

vs.

public void DoSomething(MySingleton singleton)
{
    singleton.Work();
}
DoSomething(MySingleton.instance);

Kasus 1 mengarah ke kopling tinggi, dll; Cara ke-2 tidak memiliki masalah yang dijelaskan oleh @Aaronaught, sejauh yang saya tahu. Ini semua tentang bagaimana Anda menggunakannya.

1
Evgeni