Apa Alternatif untuk Singleton


114

Kami memiliki kelas yang menyimpan informasi konfigurasi untuk aplikasi. Itu dulu tunggal. Setelah beberapa tinjauan arsitektural, kami diberitahu untuk menghapus singleton tersebut. Kami memang melihat beberapa keuntungan dari tidak menggunakan tunggal dalam pengujian unit karena kami dapat menguji konfigurasi yang berbeda sekaligus.

Tanpa tunggal, kita harus meneruskan contoh di mana-mana dalam kode kita. Ini menjadi sangat berantakan jadi kami menulis pembungkus tunggal. Sekarang kita mem-porting kode yang sama ke PHP dan .NET, saya bertanya-tanya apakah ada pola yang lebih baik yang dapat kita gunakan untuk objek konfigurasi.

Jawaban:


131

The Pengujian Google blog memiliki serangkaian entri tentang menghindari Singleton (dalam rangka menciptakan kode diuji). Mungkin ini bisa membantu Anda:

Artikel terakhir menjelaskan secara detail cara memindahkan kreasi objek baru ke dalam pabrik, sehingga Anda dapat menghindari penggunaan single. Layak dibaca pasti.

Singkatnya, kami memindahkan semua operator baru ke pabrik. Kami mengelompokkan semua objek yang memiliki masa pakai yang sama ke dalam satu pabrik.


3
*** Menggunakan injeksi ketergantungan untuk menghindari lajang
Justin

Artikel-artikel ini sebagus Standar Pemrograman Google C ++!

2
Yah, tidak juga. Saran 'Jangan gunakan metode statis' bertentangan langsung dengan prinsip antarmuka minimal Scott Meyers / Herb Sutters, misalnya. Ada nasihat yang berguna, tetapi kurang memiliki kontribusi dari banyak pikiran.
Matthieu M.

@FrankS mengapa Anda mengganti urutan tautan? Itu dalam kronologis yang baik pada awalnya.
cregox

@Cawas sebenarnya tidak tahu, itu lebih dari empat tahun yang lalu, jadi saya kira saya punya beberapa alasan untuk itu saat itu :-)
FrankS

15

Cara terbaik adalah menggunakan pola Pabrik. Saat Anda membuat instance baru dari kelas Anda (di pabrik), Anda dapat memasukkan data 'global' ke dalam objek yang baru dibangun, baik sebagai referensi ke satu instance (yang Anda simpan di kelas pabrik) atau dengan menyalin yang relevan data ke objek baru.

Semua objek Anda kemudian akan berisi data yang digunakan untuk hidup di singleton. Saya rasa tidak ada banyak perbedaan secara keseluruhan, tetapi ini dapat membuat kode Anda lebih mudah dibaca.


1
Saya tidak setuju dengan pernyataan "cara terbaik", tetapi memberi +1 untuk alternatif yang baik.
tylermac

Masalah dengan pendekatan ini adalah bahwa setiap objek baru berisi (atau referensi) apa yang berpotensi menjadi kumpulan data yang sangat besar. var_dump () pada salah satu objek yang mengandung gob tersebut menghasilkan dengan sangat cepat dalam daftar besar yang dibumbui dengan peringatan rekursi . Itu sangat jelek, tidak bisa terlalu efisien, dan membuat segalanya tampak kacau. Namun, secara pribadi saya belum menemukan cara yang lebih baik. Saya membengkokkan metode "pabrik" ke dalam menggunakan __construct () untuk mereferensikan global. Rasanya seperti semuanya membungkuk ke belakang, bagaimanapun, untuk menghindari Singleton yang ditakuti ...
FYA

2
@EastGhostCom: sebaiknya kita menggunakan singleton dan berhenti mencoba mempersulit diri kita sendiri :)
gbjbaanb

5

Saya mungkin menyatakan yang sudah jelas di sini, tetapi adakah alasan mengapa Anda tidak dapat menggunakan kerangka kerja injeksi ketergantungan seperti Spring atau Guice ? (Saya yakin Spring juga tersedia untuk .NET juga sekarang).

Dengan begitu, kerangka kerja dapat menyimpan satu salinan objek konfigurasi, dan kacang Anda (layanan, DAO, apa pun) tidak perlu khawatir mencarinya.

Ini adalah pendekatan yang biasanya saya ambil!


4

Jika Anda menggunakan Spring Framework , Anda bisa membuat kacang biasa. Secara default (atau jika Anda secara eksplisit mengatur scope="singleton") hanya satu contoh kacang dibuat dan contoh itu dikembalikan setiap kali kacang digunakan dalam ketergantungan atau diambil melalui getBean().

Anda mendapatkan keuntungan dari instance tunggal, tanpa penggandengan pola Singleton.


3
Oh ironisnya - gunakan kacang musim semi (tunggal) untuk menggantikan singleton Anda ...
Zack Macomber

4

Alternatifnya adalah menyampaikan apa yang Anda butuhkan alih-alih meminta objek untuk sesuatu.


4

jangan mengumpulkan tanggung jawab ke satu objek konfigurasi karena akan berakhir di objek yang sangat besar yang sulit dipahami dan rapuh.

Misalnya jika Anda membutuhkan parameter lain untuk kelas tertentu Anda mengubah Configurationobjek, kemudian mengkompilasi ulang semua kelas yang menggunakannya. Ini agak bermasalah.

Coba refactoring kode Anda untuk menghindari objek umum, global, dan besar Configuration. Meneruskan hanya parameter yang diperlukan ke kelas klien:

class Server {

    int port;

    Server(Configuration config) {
        this.port = config.getServerPort();
    } 

}

harus diubah menjadi:

 class Server {

    public Server(int port) {
       this.port = port;
    }
 }

sebuah kerangka kerja injeksi ketergantungan akan membantu banyak di sini, tetapi tidak stricly diperlukan.


Ya itu poin yang sangat bagus. Saya pernah melakukan ini sebelumnya. Objek konfigurasi besar saya mengimplementasikan antarmuka seperti MailServiceConf, ServerConf .. daripada kerangka kerja Dependency Injection meneruskan konfigurasi ke kelas sehingga kelas saya tidak bergantung pada objek Konfigurasi besar.
caltuntas

1

Anda dapat mencapai perilaku tunggal yang sama dengan menggunakan metode statis. Steve yegge menjelaskannya dengan sangat baik di posting ini .


Sebenarnya artikel tersebut cukup bagus, dan tidak dikatakan Anda harus menggunakan metode statis sebagai gantinya. Sebaliknya dia menyatakan bahwa metode statis hanya tunggal juga dan pada akhirnya dia merekomendasikan menggunakan pola metode pabrik: "Saya akan menutup dengan mengatakan bahwa jika Anda masih merasa perlu untuk menggunakan objek Singleton, pertimbangkan untuk menggunakan pola Metode Pabrik sebagai gantinya. ... "
FrankS

0

Apakah kelas yang hanya berisi metode dan kolom statis mungkin? Saya tidak yakin persis seperti apa situasi Anda, tetapi mungkin ada baiknya untuk memeriksanya.


1
Jika kelas tersebut stateless, itu harus menjadi kelas statis.
AlbertoPL

1
Ini ada di C ++ - polanya dikenal sebagai Monostate.

0

Tergantung pada tooling / framework, dll. Yang digunakan. Dengan alat injeksi ketergantungan / ioc, seseorang sering kali masih bisa mendapatkan kinerja / pengoptimalan tunggal dengan meminta wadah di / ioc menggunakan perilaku tunggal untuk kelas yang diperlukan - (seperti antarmuka IConfigSettings) dengan hanya membuat satu instance kelas. Ini masih bisa diganti untuk pengujian

Alternatifnya, seseorang dapat menggunakan pabrik untuk membuat kelas dan mengembalikan instance yang sama setiap kali Anda memintanya - tetapi untuk mengujinya, ia dapat mengembalikan versi stub / tiruan


0

Tinjau kemungkinan untuk membuat konfigurasi sebagai antarmuka panggilan balik. Jadi, kode sensitif konfigurasi Anda akan terlihat:

MyReuseCode.Configure(IConfiguration)

Kode system-init akan terlihat:

Library.init(MyIConfigurationImpl)

0

Anda dapat menggunakan framework injeksi dependensi untuk mengurangi kesulitan dalam meneruskan objek konfigurasi. Yang layak adalah ninject yang memiliki keuntungan menggunakan kode daripada xml.


0

Mungkin juga tidak terlalu bersih, tetapi Anda mungkin bisa meneruskan bit informasi yang ingin Anda ubah ke metode yang membuat singleton - daripada menggunakan

public static Singleton getInstance() {
    if(singleton != null)
        createSingleton();
        return singleton;
    }
}

Anda dapat memanggil createSingleton(Information info)secara langsung pada startup aplikasi (dan dalam setUp-Methods dari tes unit).


-1

Lajang tidak jahat tetapi pola desainnya cacat. Saya memiliki kelas yang saya hanya ingin membuat satu contoh selama runtime tetapi ingin membuat beberapa contoh terisolasi selama pengujian unit untuk memastikan hasil deterministik.

D menggunakan Spring, dll, adalah pilihan yang sangat bagus tetapi bukan satu-satunya pilihan.

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.