it-swarm-id.com

Kapan Singleton pantas?

Beberapa orang berpendapat bahwa Pola Singleton selalu merupakan anti-pola. Bagaimana menurut anda?

67
Fishtoaster

Dua kritik utama Singletons jatuh ke dalam dua kubu dari apa yang saya amati:

  1. Singletons disalahgunakan dan disalahgunakan oleh programmer yang kurang mampu sehingga semuanya menjadi singleton dan Anda melihat kode dikotori dengan referensi Class :: get_instance (). Secara umum hanya ada satu atau dua sumber daya (seperti koneksi database misalnya) yang memenuhi syarat untuk penggunaan pola Singleton.
  2. Lajang pada dasarnya adalah kelas statis, mengandalkan satu atau lebih metode dan properti statis. Semua hal statis menghadirkan masalah nyata dan nyata ketika Anda mencoba melakukan Unit Testing karena mereka mewakili jalan buntu dalam kode Anda yang tidak dapat diejek atau di-stub. Akibatnya, ketika Anda menguji kelas yang bergantung pada Singleton (atau metode statis lain atau kelas) Anda tidak hanya menguji kelas itu tetapi juga metode atau kelas statis.

Sebagai hasil dari kedua hal ini, pendekatan umum adalah dengan menggunakan membuat objek kontainer luas untuk menampung satu instance dari kelas-kelas ini dan hanya objek kontainer yang memodifikasi tipe-tipe kelas ini sementara banyak kelas lain dapat diberikan akses kepada mereka untuk menggunakan dari objek wadah.

32
Noah Goodrich

Saya setuju bahwa ini anti-pola. Mengapa? Karena memungkinkan kode Anda untuk berbohong tentang dependensinya, dan Anda tidak dapat mempercayai programmer lain untuk tidak memperkenalkan keadaan yang bisa berubah dalam singleton Anda yang sebelumnya tidak dapat diubah.

Kelas mungkin memiliki konstruktor yang hanya membutuhkan string, jadi Anda pikir itu dipakai secara terpisah dan tidak memiliki efek samping. Namun, diam-diam, itu berkomunikasi dengan semacam objek singleton publik yang tersedia secara global, sehingga setiap kali Anda membuat instance kelas, itu berisi data yang berbeda. Ini adalah masalah besar, tidak hanya untuk pengguna API Anda, tetapi juga untuk pengujian kode. Untuk unit-test kode dengan benar, Anda perlu mengelola mikro dan menyadari keadaan global dalam singleton, untuk mendapatkan hasil pengujian yang konsisten.

42
Magnus Wolffelt

Pola Singleton pada dasarnya hanya variabel global malas diinisialisasi. Variabel global secara umum dan benar dianggap jahat karena memungkinkan tindakan seram pada jarak antara bagian-bagian yang tampaknya tidak terkait suatu program. Namun, IMHO tidak ada yang salah dengan variabel global yang ditetapkan sekali, dari satu tempat, sebagai bagian dari rutin inisialisasi program (misalnya, dengan membaca file konfigurasi atau argumen baris perintah) dan diperlakukan sebagai konstanta setelahnya. Penggunaan variabel global semacam itu hanya berbeda dalam huruf, bukan dalam semangat, dari memiliki konstanta bernama yang dinyatakan pada waktu kompilasi.

Demikian pula, pendapat saya tentang Singletons adalah bahwa mereka buruk jika dan hanya jika mereka digunakan untuk melewati keadaan yang bisa berubah antara bagian-bagian yang tampaknya tidak berhubungan dari suatu program. Jika mereka tidak mengandung keadaan bisa berubah, atau jika keadaan bisa berubah yang mereka mengandung benar-benar dienkapsulasi sehingga pengguna objek tidak perlu mengetahuinya bahkan dalam lingkungan multithreaded, maka tidak ada yang salah dengan mereka.

34
dsimcha

Mengapa orang menggunakannya?

Saya telah melihat cukup banyak lajang di dunia PHP. Saya tidak ingat ada kasus penggunaan di mana saya menemukan pola untuk dibenarkan. Tapi saya pikir saya punya ide tentang motivasi mengapa orang memang menggunakannya.

  1. Satu instance.

    "Gunakan satu instance kelas C di seluruh aplikasi."

    Ini adalah persyaratan yang wajar, mis. untuk "koneksi database default". Itu tidak berarti Anda tidak akan pernah membuat koneksi db kedua, itu hanya berarti Anda biasanya bekerja dengan yang standar.

  2. Satu Instansiasi.

    "Jangan izinkan kelas C untuk dipakai lebih dari satu kali (per proses, per permintaan, dll)."

    Ini hanya relevan jika membuat instance kelas akan memiliki efek samping yang bertentangan dengan instance lain.

    Seringkali konflik ini dapat dihindari dengan mendesain ulang komponen - mis. dengan menghilangkan efek samping dari konstruktor kelas. Atau mereka dapat diselesaikan dengan cara lain. Tetapi mungkin masih ada beberapa kasus penggunaan yang sah.

    Anda juga harus memikirkan apakah persyaratan "hanya satu" benar-benar berarti "satu per proses". Misalnya. untuk konkurensi sumber daya, persyaratannya agak "satu per seluruh sistem, lintas proses" dan bukan "satu per proses". Dan untuk hal-hal lain itu lebih per "konteks aplikasi", dan Anda hanya memiliki satu konteks aplikasi per proses.

    Kalau tidak, tidak perlu memaksakan asumsi ini. Menegakkan ini juga berarti Anda tidak dapat membuat turunan terpisah untuk pengujian unit.

  3. Akses global.

    Ini sah hanya jika Anda tidak memiliki infrastruktur yang memadai untuk meneruskan benda-benda ke tempat mereka digunakan. Ini bisa berarti bahwa kerangka kerja atau lingkungan Anda payah, tetapi mungkin tidak dalam kemampuan Anda untuk memperbaikinya.

    Harganya ketat, ketergantungan tersembunyi, dan segala sesuatu yang buruk tentang keadaan global. Tetapi Anda mungkin sudah menderita ini.

  4. Instantiasi malas.

    Ini bukan bagian penting dari singleton, tetapi tampaknya cara paling populer untuk mengimplementasikannya. Tetapi, sementara instantiasi yang malas adalah hal yang baik untuk dimiliki, Anda tidak benar-benar membutuhkan singleton untuk mencapainya.

Implementasi yang khas

Implementasi tipikal adalah kelas dengan konstruktor pribadi, dan variabel instance statis, dan metode getInstance () statis dengan instantiation lazy.

Selain masalah yang disebutkan di atas, ini menggigit dengan prinsip tanggung jawab tunggal , karena kelas melakukan mengendalikan instantiation sendiri dan siklus hidup , di samping tanggung jawab lain yang kelas sudah ada.

Kesimpulan

Dalam banyak kasus, Anda dapat mencapai hasil yang sama tanpa tunggal, dan tanpa negara global. Sebagai gantinya, Anda harus menggunakan injeksi ketergantungan, dan Anda mungkin ingin mempertimbangkan wadah injeksi ketergantungan .

Namun, ada kasus penggunaan di mana Anda memiliki persyaratan valid berikut yang tersisa:

  • Instansi tunggal (tetapi bukan instantiasi tunggal)
  • Akses global (karena Anda bekerja dengan kerangka usia perunggu)
  • Instansiasi malas (karena itu hanya baik untuk memiliki)

Jadi, inilah yang dapat Anda lakukan dalam kasus ini:

  • Buat kelas C yang ingin Anda instantiate, dengan konstruktor publik.

  • Buat kelas S terpisah dengan variabel instance statis dan metode S :: getInstance () statis dengan malas instantiation, yang akan menggunakan kelas C untuk instance.

  • Hilangkan semua efek samping dari konstruktor C. Sebagai gantinya, letakkan efek samping ini dalam metode S :: getInstance ().

  • Jika Anda memiliki lebih dari satu kelas dengan persyaratan di atas, Anda dapat mempertimbangkan untuk mengelola instance kelas dengan wadah layanan lokal kecil , dan menggunakan instance statis hanya untuk wadah. Jadi, S :: getContainer () akan memberi Anda wadah layanan malas-instantiated, dan Anda mendapatkan objek lain dari wadah.

  • Hindari memanggil getInstance statis () di mana Anda bisa. Gunakan injeksi ketergantungan sebagai gantinya, bila memungkinkan. Terutama, jika Anda menggunakan pendekatan wadah dengan banyak objek yang saling bergantung, maka tidak ada yang harus memanggil S :: getContainer ().

  • Secara opsional buat antarmuka yang mengimplementasikan kelas C, dan gunakan ini untuk mendokumentasikan nilai kembalinya S :: getInstance ().

(Apakah kita masih menyebut ini singleton? Saya serahkan ini ke bagian komentar ..)

Manfaat:

  • Anda dapat membuat instance C terpisah untuk pengujian unit, tanpa menyentuh status global apa pun.

  • Manajemen instance dipisahkan dari kelas itu sendiri -> pemisahan masalah, prinsip tanggung jawab tunggal.

  • Akan sangat mudah untuk membiarkan S :: getInstance () menggunakan kelas yang berbeda untuk instance, atau bahkan secara dinamis menentukan kelas mana yang akan digunakan.

7
donquixote

Secara pribadi saya akan menggunakan lajang ketika saya membutuhkan 1, 2, atau 3, atau sejumlah objek untuk kelas tertentu yang dimaksud. Atau saya ingin menyampaikan kepada pengguna kelas saya bahwa saya tidak ingin beberapa instance kelas saya dibuat agar berfungsi dengan baik.

Juga saya hanya akan menggunakannya ketika saya perlu menggunakannya hampir di mana-mana dalam kode saya dan saya tidak ingin meneruskan objek sebagai parameter ke setiap kelas atau fungsi yang membutuhkannya.

Selain itu saya hanya akan menggunakan singleton jika tidak merusak transparansi referensial fungsi lain. Artinya diberi beberapa input maka akan selalu menghasilkan output yang sama. Yaitu. Saya tidak menggunakannya untuk negara global. Kecuali jika keadaan global itu diinisialisasi satu kali dan tidak pernah berubah.

Adapun kapan tidak menggunakannya, lihat 3 di atas dan ubah ke sebaliknya.

1
Brian R. Bondy