Bagaimana saya bisa menghindari memiliki banyak lajang dalam arsitektur gim saya?

55

Saya menggunakan mesin game cocos2d-x untuk membuat game. Mesinnya sudah menggunakan banyak lajang. Jika seseorang menggunakannya, maka mereka harus terbiasa dengan beberapa di antaranya:

Director
SimpleAudioEngine
SpriteFrameCache
TextureCache
EventDispatcher (was)
ArmatureDataManager
FileUtils
UserDefault

dan banyak lagi dengan keseluruhan sekitar 16 kelas. Anda dapat menemukan daftar serupa di halaman ini: Benda-benda singleton di Cocos2d-html5 v3.0 Tetapi ketika saya ingin menulis permainan saya, saya membutuhkan lebih banyak lajang:

PlayerData (score, lives, ...)
PlayerProgress (passed levels, stars)
LevelData (parameters per levels and level packs)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
GameData (you may obtain some data from server to configure the game)
IAP (for in purchases)
Ads (for showing ads)
Analytics (for collecting some analytics)
EntityComponentSystemManager (mananges entity creation and manipulation)
Box2dManager (manages the physics world)
.....

Mengapa saya pikir mereka harus menjadi lajang? Karena saya akan membutuhkannya di tempat yang sangat berbeda dalam permainan saya, dan akses bersama akan sangat berguna. Dengan kata lain, saya tidak harus membuatnya di suatu tempat dan meneruskan petunjuk ke semua arsitektur saya karena akan sangat sulit. Juga ini adalah hal-hal yang saya hanya perlu satu. Bagaimanapun saya membutuhkan beberapa, saya dapat menggunakan pola Multiton juga. Tetapi yang terburuk adalah bahwa Singleton adalah pola yang paling banyak dikritik karena:

 - bad testability
 - no inheritance available
 - no lifetime control
 - no explicit dependency chain
 - global access (the same as global variables, actually)
 - ....

Anda dapat menemukan beberapa pemikiran di sini: https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons dan https://stackoverflow.com/questions/4074154/when-should-the-singleton -pola-tidak-digunakan-selain-yang-jelas

Jadi, saya pikir, saya melakukan sesuatu yang salah. Saya pikir kode saya berbau . :) Saya ingat bagaimana pengembang game yang lebih berpengalaman memecahkan masalah arsitektur ini? Saya ingin memeriksa, mungkin masih normal dalam pengembangan game untuk memiliki lebih dari 30 lajang , dianggap yang sudah ada di mesin game.

Saya telah berpikir untuk menggunakan Singleton-Facade yang akan memiliki turunan dari semua kelas yang saya butuhkan, tetapi masing-masing dari mereka belum akan menjadi lajang. Ini akan menghilangkan banyak masalah, dan saya hanya akan memiliki satu singleton yang akan menjadi Fasad itu sendiri. Tetapi dalam hal ini saya akan memiliki masalah desain lain. Fasad akan menjadi TUJUAN ALLAH. Saya pikir ini juga baunya . Jadi saya tidak dapat menemukan solusi desain yang bagus untuk situasi ini. Tolong saran.

Narek
sumber
26
Kebanyakan orang yang menentang Singleton tampaknya tidak menyadari bahwa pekerjaan yang diperlukan untuk menyebarkan beberapa data / fungsionalitas melalui semua kode mungkin lebih merupakan sumber bug daripada menggunakan Singleton. Jadi pada dasarnya mereka tidak membandingkan, hanya menolak ==> saya menduga anti-Singletonisme sebagai Postur :-) Baiklah, Singleton memiliki kekurangan, jadi jika Anda dapat menghindarinya, lakukanlah sehingga Anda terhindar dari masalah yang menyertainya , sekarang jika Anda tidak bisa, lakukan tanpa melihat ke belakang. Lagipula, Cocos2d tampaknya bekerja cukup baik dengan 16 Singletons ...
GameAlchemist
6
Sebagai pengingat, ini adalah pertanyaan tentang bagaimana melakukan tugas tertentu. Ini bukan pertanyaan yang meminta diskusi pro / kontra tentang lajang (yang akan menjadi off-topic di sini). Selanjutnya, ingat bahwa komentar harus digunakan untuk meminta klarifikasi dari penanya, bukan sebagai platform untuk diskusi tangensial tentang pro dan kontra dari para lajang. Jika Anda ingin memberikan masukan, cara yang tepat untuk melakukannya adalah dengan memberikan jawaban yang sah untuk pertanyaan tersebut , di mana Anda dapat meredam solusi Anda dengan saran untuk atau menentang pola tunggal.
Josh
Saya berasumsi bahwa lajang ini kemudian dapat diakses secara global? Yang akan menjadi salah satu alasan untuk menghindari pola itu.
Peter - Reinstate Monica

Jawaban:

41

Saya menginisialisasi layanan saya di kelas aplikasi utama saya dan kemudian meneruskannya sebagai petunjuk untuk apa pun yang perlu menggunakannya baik melalui konstruktor atau fungsi. Ini berguna karena dua alasan.

Pertama, urutan inisialisasi dan pembersihan sederhana dan jelas. Tidak ada cara untuk secara tidak sengaja menginisialisasi satu layanan di tempat lain seperti Anda dapat menggunakan singleton.

Dua, jelas siapa yang bergantung pada apa. Saya dapat dengan mudah melihat kelas apa yang membutuhkan layanan apa saja dari deklarasi kelas.

Anda mungkin berpikir menjengkelkan untuk melewatkan semua objek ini, tetapi jika sistem dirancang dengan baik, itu benar-benar tidak buruk sama sekali dan membuat segalanya lebih jelas (bagi saya toh).

megadan
sumber
34
+1. Lebih jauh, "gangguan" karena harus meneruskan referensi ke sistem di sekitar membantu untuk mengatasi ketergantungan ketergantungan; jika Anda harus menyerahkan sesuatu ke " semuanya ," itu pertanda baik bahwa Anda memiliki masalah kopling yang buruk dalam desain Anda, dan itu jauh lebih eksplisit dengan cara ini daripada dengan akses global yang bebas ke segala sesuatu dari mana-mana.
Josh
2
Ini sebenarnya tidak memberikan solusi ... sehingga Anda memiliki kelas program Anda dengan banyak objek tunggal di dalamnya dan sekarang 10 level dalam kode, beberapa sesi dalam adegan membutuhkan salah satu dari para lajang itu ... bagaimana cara melewatinya?
Perang
Wardy: Mengapa mereka harus menjadi lajang? Tentu, sebagian besar Anda mungkin berkeliling contoh tertentu, tetapi apa yang membuat singleton menjadi singleton adalah bahwa Anda TIDAK BISA menggunakan contoh yang berbeda. Dengan injeksi, Anda dapat melewatkan satu instance untuk satu objek dan lainnya untuk yang berikutnya. Hanya karena Anda tidak melakukan itu dengan alasan apa pun, tidak berarti Anda tidak bisa melakukannya.
parar
3
Mereka bukan orang lajang. Mereka adalah objek reguler seperti yang lainnya dengan pemilik yang didefinisikan dengan baik yang mengontrol init / pembersihan. Jika Anda 10 level dalam tanpa akses, mungkin Anda memiliki masalah desain. Tidak semua kebutuhan atau harus memiliki akses ke penyaji, audio, dan semacamnya. Dengan cara ini membuat Anda berpikir tentang desain Anda. Sebagai bonus tambahan bagi mereka yang menguji kode mereka (apa ??), pengujian unit juga menjadi lebih mudah.
megadan
2
Ya, saya pikir injeksi ketergantungan (dengan atau tanpa kerangka kerja seperti Spring) adalah solusi sempurna untuk menangani ini. Saya menemukan ini menjadi artikel yang bagus untuk mereka yang bertanya-tanya bagaimana cara menyusun kode yang lebih baik agar tidak lewat di mana-mana: misko.hevery.com/2008/10/21/…
megadan
17

Saya tidak akan membahas tentang kejahatan di balik lajang karena Internet dapat melakukan itu lebih baik daripada saya.

Dalam permainan saya, saya menggunakan pola Service Locator untuk menghindari memiliki banyak Lajang / Manajer.

Konsepnya cukup sederhana. Anda hanya memiliki satu Singleton yang bertindak seperti satu-satunya antarmuka untuk mencapai apa yang Anda gunakan sebagai Singleton. Alih-alih memiliki beberapa Singleton Anda sekarang hanya memiliki satu.

Panggilan seperti:

FlyingToasterManager.GetInstance().Toast();
SmokeAlarmManager.GetInstance().Start();

Sepertinya ini, menggunakan pola Layanan Locator:

SvcLocator.FlyingToaster.Toast();
SvcLocator.SmokeAlarm.Start();

Seperti yang Anda lihat, setiap Singleton terkandung di dalam Service Locator.

Untuk memiliki penjelasan yang lebih rinci saya sarankan Anda membaca halaman ini tentang cara menggunakan Pola Locator .

[ EDIT ]: seperti yang ditunjukkan dalam komentar, situs gameprogrammingpatterns.com juga berisi bagian yang sangat menarik tentang Singleton .

Saya harap ini membantu.

lvictorino
sumber
4
Situs yang tertaut ke sini, gameprogrammingpatterns.com, juga memiliki bab tentang Singletons yang tampaknya relevan.
ajp15243
28
Pencari layanan hanyalah orang lajang yang memindahkan semua masalah normal ke runtime alih-alih waktu kerja. Namun, apa yang Anda sarankan hanyalah registry statis raksasa, yang hampir lebih baik, tetapi pada dasarnya masih berupa variabel global. Ini hanyalah masalah arsitektur yang buruk jika banyak level memerlukan akses ke objek yang sama. Injeksi ketergantungan yang tepat adalah yang dibutuhkan di sini, tidak berbeda dengan global yang disebut global saat ini.
Magus
2
Bisakah Anda menguraikan "injeksi ketergantungan yang tepat"?
Blue Wizard
17
Ini tidak terlalu membantu masalah ini. Pada dasarnya banyak lajang yang bergabung menjadi satu singleton raksasa. Tak satu pun dari masalah struktural yang mendasarinya yang diperbaiki atau bahkan ditangani, kode masih bau hanya lebih cantik sekarang.
ClassicThunder
4
@classicthunder Meskipun ini tidak membahas setiap masalah tunggal, ini merupakan peningkatan besar atas Singletons karena memang mengatasi beberapa. Secara khusus, kode yang Anda tulis tidak lagi digabungkan ke satu kelas, melainkan antarmuka / kategori kelas. Sekarang Anda dapat dengan mudah bertukar berbagai implementasi berbagai layanan.
jhocking
12

Membaca semua jawaban, komentar, dan artikel yang ditunjukkan, terutama dua artikel brilian ini,

akhirnya, saya sampai pada kesimpulan berikut, yang merupakan semacam jawaban untuk pertanyaan saya sendiri. Pendekatan terbaik adalah tidak menjadi malas dan lulus ketergantungan secara langsung. Ini eksplisit, dapat diuji, tidak ada akses global, dapat menunjukkan desain yang salah jika Anda harus melewati delegasi yang sangat dalam dan di banyak tempat dan sebagainya. Tetapi dalam pemrograman satu ukuran tidak cocok untuk semua. Dengan demikian, masih ada kasus yang tidak mudah untuk dilewatkan secara langsung. Sebagai contoh jika kita membuat kerangka permainan (template kode yang akan digunakan kembali sebagai kode dasar untuk mengembangkan berbagai jenis game), dan menyertakan banyak layanan di sana. Di sini kita tidak tahu seberapa dalam setiap layanan dapat dilewati, dan berapa banyak tempat yang diperlukan. Itu tergantung pada game tertentu. Jadi apa yang kita lakukan dalam kasus seperti ini? Kami menggunakan pola desain Service Locator alih-alih Singleton,

  1. Anda tidak memprogram untuk implementasi tetapi ke antarmuka. Ini sangat penting dalam hal prinsipal OOP. Saat runtime Anda dapat mengubah atau bahkan menonaktifkan layanan menggunakan pola Obyek Null. Ini tidak hanya penting dalam hal fleksibilitas, tetapi secara langsung membantu dalam pengujian. Anda dapat menonaktifkan, mengejek, Anda juga dapat menggunakan pola Penghias, untuk menambahkan beberapa penebangan dan fungsi lainnya juga. Ini adalah properti yang sangat penting, yang tidak dimiliki Singleton.
  2. Keuntungan besar lainnya adalah Anda dapat mengontrol umur objek. Di Singleton Anda hanya mengontrol waktu objek akan dihasilkan oleh pola Inisialisasi Lazy. Tetapi begitu objek tersebut muncul, ia akan hidup sampai akhir program. Tetapi dalam beberapa kasus Anda ingin mengubah layanan. Atau bahkan mematikannya. Khususnya dalam bermain game, fleksibilitas ini sangat penting.
  3. Ini menggabungkan / membungkus beberapa Singletons menjadi satu API yang ringkas. Saya pikir Anda juga dapat menggunakan beberapa Facade like Service Locators, yang untuk satu operasi mungkin menggunakan beberapa layanan sekaligus.
  4. Anda mungkin berpendapat bahwa kami masih memiliki akses global yang merupakan masalah desain utama dengan Singleton. Saya setuju, tetapi kita akan membutuhkan pola ini hanya dan hanya jika kita menginginkan akses global. Kita tidak boleh mengeluh tentang akses global, jika kita berpikir bahwa injeksi ketergantungan langsung tidak berguna untuk situasi kita, dan kita membutuhkan akses global. Tetapi bahkan untuk masalah ini, perbaikan tersedia (lihat artikel kedua di atas). Anda dapat membuat semua layanan menjadi pribadi, dan Anda bisa mewarisi dari kelas Service Locator semua kelas yang menurut Anda harus menggunakan layanan. Ini secara signifikan dapat mempersempit akses. Dan tolong pertimbangkan bahwa dalam kasus Singleton Anda tidak dapat menggunakan warisan karena konstruktor pribadi.

Kita juga harus mempertimbangkan bahwa pola ini digunakan dalam Unity, LibGDX, XNA. Ini bukan keuntungan, tetapi ini adalah semacam bukti dari kegunaan pola. Pertimbangkan bahwa mesin ini dikembangkan oleh banyak pengembang cerdas sejak lama dan selama fase evolusi mesin, mereka masih belum menemukan solusi yang lebih baik.

Sebagai kesimpulan, saya pikir Service Locator dapat sangat berguna untuk skenario yang dijelaskan dalam pertanyaan saya, tetapi masih harus dihindari jika memungkinkan. Sebagai aturan praktis - gunakan jika Anda harus.

EDIT: Setelah satu tahun bekerja dan menggunakan DI langsung dan memiliki masalah dengan konstruktor besar dan banyak delegasi, saya telah melakukan lebih banyak penelitian dan telah menemukan artikel bagus yang berbicara tentang pro dan kontra tentang metode DI dan Service Locator. Mengutip artikel ini ( http://www.martinfowler.com/articles/injection.html ) oleh Martin Fowler yang menjelaskan kapan sebenarnya pencari Layanan buruk:

Jadi masalah utama adalah untuk orang-orang yang menulis kode yang mengharapkan untuk digunakan dalam aplikasi di luar kendali penulis. Dalam kasus ini, bahkan asumsi minimal tentang Locator Layanan adalah masalah.

Tapi bagaimanapun, jika Anda ingin kami DI pertama kali, Anda harus membaca ini http://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/ artikel (ditunjukkan oleh @megadan) , dengan memberikan perhatian yang sangat hati-hati pada Hukum Demeter (LoD) atau prinsip pengetahuan paling rendah .

Narek
sumber
Intoleransi pribadi saya terhadap pencari layanan muncul terutama dari upaya saya untuk menguji kode yang menggunakannya. Untuk kepentingan pengujian kotak hitam, Anda memanggil konstruktor untuk membuat contoh untuk menguji. Pengecualian dapat dilemparkan segera, atau pada panggilan metode apa pun, dan mungkin tidak ada properti publik untuk mengarahkan Anda ke dependensi yang hilang. Ini mungkin kecil jika Anda memiliki akses ke sumber, namun masih melanggar Prinsip Terkejut (masalah yang sering terjadi dengan Singletons). Anda telah menyarankan untuk lewat di lokasi layanan, yang mengurangi bagian dari ini, dan harus dipuji karenanya.
Magus
3

Tidak jarang bagian basis kode dianggap objek landasan atau kelas dasar, tetapi itu tidak membenarkan siklus hidupnya untuk didikte sebagai Singleton.

Programmer sering mengandalkan pola Singleton sebagai sarana kenyamanan dan kemalasan murni daripada mengambil pendekatan alternatif dan menjadi sedikit lebih verbose dan memaksakan hubungan objek antara satu sama lain dalam manor eksplisit.

Jadi tanyakan pada diri sendiri mana yang lebih mudah dirawat.

Jika Anda seperti saya, saya lebih memilih untuk dapat membuka file header dan secara singkat membaca argumen konstruktor dan metode publik untuk melihat dependensi apa yang dibutuhkan suatu objek daripada harus memeriksa ratusan baris kode dalam file implementasi.

Jika Anda telah menjadikan sebuah objek Singleton dan kemudian menyadari bahwa Anda harus mampu mendukung banyak contoh objek tersebut, bayangkan perubahan besar diperlukan jika kelas ini adalah sesuatu yang Anda gunakan cukup banyak di seluruh basis kode Anda. Alternatif yang lebih baik adalah dengan membuat dependensi menjadi eksplisit, bahkan jika objek hanya dialokasikan satu kali dan jika perlu muncul untuk mendukung banyak instance, Anda dapat melakukannya dengan aman tanpa kekhawatiran tersebut.

Seperti yang telah ditunjukkan orang lain, penggunaan pola Singleton juga mengaburkan kopling yang sering menandakan desain yang buruk dan kohesi yang tinggi antara modul. Kohesi semacam itu biasanya tidak diinginkan dan mengarah ke kode rapuh yang sulit dipertahankan.

Naro
sumber
-1: Jawaban ini secara keliru menggambarkan pola tunggal, dan semua argumennya menggambarkan masalah dengan implementasi pola yang buruk. Singleton yang tepat adalah antarmuka, dan ia menghindari setiap masalah yang Anda uraikan (termasuk peralihan ke tidak melajang, yang merupakan skenario yang ditangani GoF secara eksplisit). Jika sulit untuk refactor penggunaan singleton, maka refactoring penggunaan referensi objek eksplisit hanya bisa lebih sulit, tidak kurang. Jika singleton entah bagaimana menyebabkan kopling kode LEBIH, itu hanya dilakukan salah.
Seth Battin
1

Singleton adalah pola yang terkenal, tetapi ada baiknya mengetahui tujuan yang dilayaninya, dan pro dan kontra.

Sangat masuk akal jika tidak ada hubungan sama sekali.
Jika Anda dapat menangani komponen Anda dengan objek yang sama sekali berbeda (tidak ada ketergantungan kuat) dan berharap memiliki perilaku yang sama, singleton mungkin merupakan pilihan yang baik.

Di sisi lain, jika Anda memerlukan fungsionalitas kecil , hanya digunakan pada waktu-waktu tertentu, Anda sebaiknya memilih untuk melewatkan referensi .

Seperti yang Anda sebutkan, lajang tidak memiliki kendali seumur hidup. Tetapi keuntungannya adalah mereka memiliki inisialisasi yang malas.
Jika Anda tidak memanggil singleton itu dalam masa aplikasi Anda, itu tidak akan dipakai. Tapi saya ragu Anda membangun lajang dan tidak menggunakannya.

Anda harus melihat singleton seperti layanan, bukan komponen .

Singletons bekerja, mereka tidak pernah merusak aplikasi apa pun. Tetapi kekurangan mereka (empat yang Anda berikan) adalah alasan Anda tidak akan membuatnya.

Gila
sumber
1

Mesinnya sudah menggunakan banyak lajang. Jika seseorang menggunakannya, maka mereka harus terbiasa dengan beberapa di antaranya:

Ketidaktahuan bukanlah alasan mengapa lajang perlu dihindari.

Ada banyak alasan bagus mengapa Singleton diperlukan atau tidak dapat dihindari. Kerangka permainan sering menggunakan lajang karena merupakan konsekuensi yang tidak terhindarkan dari hanya memiliki satu perangkat keras stateful. Tidak masuk akal untuk pernah ingin mengendalikan perangkat keras ini dengan beberapa contoh penangan masing-masing. Permukaan grafik adalah perangkat keras stateful eksternal dan secara tidak sengaja menginisialisasi salinan kedua dari subsistem grafis tidak kekurangan bencana, karena sekarang dua subsistem grafis akan bertengkar satu sama lain tentang siapa yang akan menggambar dan kapan, menimpa satu sama lain secara tak terkendali. Demikian juga dengan sistem acara antrian, mereka akan berebut siapa yang mendapatkan acara mouse dengan cara nondeterministic. Ketika berhadapan dengan perangkat keras eksternal stateful di mana hanya ada satu dari mereka, singleton tidak dapat dihindari untuk mencegah konflik.

Tempat lain di mana Singleton masuk akal adalah dengan manajer Cache. Tembolok adalah kasus khusus. Mereka seharusnya tidak benar-benar dianggap sebagai Singleton, bahkan ketika mereka menggunakan semua teknik yang sama seperti Singletons untuk tetap hidup dan hidup selamanya. Layanan caching adalah layanan transparan, mereka tidak seharusnya mengubah perilaku program, jadi jika Anda mengganti layanan cache dengan cache nol, program harus tetap berfungsi, kecuali bahwa itu hanya berjalan lebih lambat. Alasan utama mengapa manajer cache pengecualian untuk singleton adalah karena tidak masuk akal untuk mematikan layanan cache sebelum mematikan aplikasi itu sendiri karena itu juga akan membuang objek yang di-cache, yang mengalahkan titik memilikinya sebagai singleton.

Itulah alasan bagus mengapa layanan ini masih lajang. Namun, tidak ada alasan yang baik untuk memiliki lajang berlaku untuk kelas yang telah Anda daftarkan.

Karena saya akan membutuhkannya di tempat yang sangat berbeda dalam permainan saya, dan akses bersama akan sangat berguna.

Itu bukan alasan untuk lajang. Itulah alasan global. Ini juga merupakan tanda desain yang buruk jika Anda harus melewati banyak hal di berbagai sistem. Harus melewati hal-hal di sekitar menunjukkan kopling tinggi yang dapat dicegah dengan desain OO yang baik.

Hanya dari melihat daftar kelas di postingan Anda yang menurut Anda perlu lajang, saya dapat mengatakan bahwa setengah dari mereka benar-benar tidak boleh lajang dan setengah lainnya sepertinya mereka bahkan tidak seharusnya ada di sana. Bahwa Anda perlu melewati objek tampaknya disebabkan oleh kurangnya enkapsulasi yang tepat daripada kasus penggunaan yang baik untuk lajang.

Mari kita lihat kelas Anda sedikit demi sedikit:


PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)

Hanya dari nama-nama kelas, saya akan mengatakan bahwa kelas-kelas ini berbau seperti antipattern Model Domain Anemic. Objek anemia biasanya menghasilkan banyak data yang perlu dilewatkan yang meningkatkan kopling dan membuat sisa kode rumit. Juga, kelas anemia menyembunyikan fakta bahwa Anda mungkin masih berpikir secara prosedural daripada menggunakan orientasi objek untuk merangkum detail.


IAP (for in purchases)
Ads (for showing ads)

Mengapa kelas-kelas ini harus lajang? Tampaknya bagi saya ini adalah kelas yang berumur pendek, yang harus dimunculkan ketika dibutuhkan dan didekonstruksi ketika pengguna menyelesaikan pembelian atau ketika iklan tidak perlu lagi ditampilkan.


EntityComponentSystemManager (mananges entity creation and manipulation)

Dengan kata lain, sebuah konstruktor dan lapisan layanan? Mengapa kelas ini tanpa batasan atau tujuan yang jelas bahkan ada di tempat pertama?


PlayerProgress (passed levels, stars)

Mengapa perkembangan pemain terpisah dari kelas Player? Kelas Player harus tahu cara melacak kemajuannya sendiri, jika Anda ingin menerapkan pelacakan kemajuan di kelas yang berbeda dari kelas Player untuk pemisahan tanggung jawab, maka PlayerProgress harus berada di belakang Player.


Box2dManager (manages the physics world)

Saya benar-benar tidak dapat berkomentar lebih jauh tentang ini tanpa mengetahui apa yang sebenarnya dilakukan kelas ini, tetapi satu hal yang jelas adalah bahwa kelas ini tidak disebutkan namanya.


Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)

Ini sepertinya satu-satunya kelas di mana Singleton mungkin masuk akal, karena objek koneksi sosial sebenarnya bukan objek Anda. Koneksi sosial hanyalah bayangan dari objek eksternal yang hidup dalam layanan jarak jauh. Dengan kata lain, ini adalah semacam cache. Pastikan Anda dengan jelas memisahkan bagian caching dengan bagian layanan dari layanan eksternal, karena bagian terakhir seharusnya tidak perlu menjadi singleton.

Salah satu cara Anda dapat menghindari lewat instance dari kelas-kelas ini di sekitar adalah dengan menggunakan message passing. Daripada melakukan panggilan langsung ke instance dari kelas-kelas ini, alih-alih Anda mengirim pesan yang ditujukan ke layanan Analytic dan SocialConnection secara tidak sinkron, dan layanan ini berlangganan untuk menerima pesan-pesan ini dan bertindak berdasarkan pesan tersebut. Karena antrian acara sudah menjadi singleton, dengan menginjak-injak panggilan, Anda menghindari keharusan menyampaikan contoh aktual saat berkomunikasi dengan singleton.

Lie Ryan
sumber
-1

Lajang bagi saya SELALU buruk.

Dalam arsitektur saya, saya membuat koleksi GameServices yang dikelola kelas permainan. Saya dapat mencari tambah dan hapus dari koleksi ini sesuai kebutuhan.

Dengan mengatakan sesuatu berada dalam lingkup global, Anda pada dasarnya mengatakan "hing ini adalah tuhan dan tidak memiliki tuan" dalam contoh yang Anda berikan di atas, saya akan mengatakan sebagian besar dari mereka adalah kelas pembantu atau GameServices yang dalam hal ini permainan akan bertanggung jawab untuk mereka.

Tapi itu hanya desain saya di mesin saya, situasi Anda mungkin berbeda.

Menempatkannya lebih langsung ... Satu-satunya singleton nyata adalah permainan karena memiliki setiap bagian dari kode.

Jawaban ini didasarkan pada sifat subyektif dari pertanyaan dan didasarkan murni pada pendapat saya, mungkin tidak benar untuk semua pengembang di luar sana.

Edit: Seperti yang diminta ...

Ok ini dari kepala saya karena saya tidak memiliki kode saya di depan saya saat ini tetapi pada dasarnya di akar setiap permainan adalah kelas Game yang benar-benar tunggal ... itu pasti!

Jadi saya menambahkan yang berikut ...

class Game
{
   IList<GameService> Services;

   public T GetService<T>() where T : GameService
   {
       return Services.FirstOrDefatult(s => s is T);
   }
}

Sekarang saya juga memiliki kelas sistem (statis) yang berisi semua lajang saya dari seluruh program (berisi sangat sedikit, permainan dan opsi konfigurasi dan hanya itu saja) ...

public static class Sys
{
    // only the main game assembly can set this (done by the game ctor)
    // anything can get
    public static Game Game { get; internal set; }
}

Dan penggunaan ...

Dari mana saja saya dapat melakukan sesuatu seperti

var tService = Sys.Game.getService<T>();
tService.Something();

Pendekatan ini berarti bahwa permainan saya "Milik" hal-hal yang biasanya akan dianggap sebagai "singleton" dan saya dapat memperluas kelas dasar GameService seperti yang saya inginkan. Saya memiliki antarmuka seperti IUpdateable yang diperiksa permainan saya dan memanggil layanan apa pun yang mengimplementasikannya sesuai kebutuhan untuk situasi tertentu.

Saya juga memiliki layanan yang mewarisi / memperluas layanan lain untuk skenario pemandangan yang lebih spesifik.

Sebagai contoh ...

Saya memiliki layanan Fisika yang menangani simulasi fisika saya tetapi tergantung pada adegan simulasi fisika mungkin memerlukan beberapa perilaku yang sedikit berbeda.

Perang
sumber
1
Bukankah seharusnya layanan game hanya dipakai satu kali saja? Jika demikian, itu hanya pola tunggal yang tidak ditegakkan di tingkat kelas, yang lebih buruk daripada pola tunggal yang diterapkan dengan benar.
GameAlchemist
2
@Wardy Saya tidak mengerti dengan jelas apa yang telah Anda lakukan tetapi kedengarannya seperti Singleton-Facade yang telah saya jelaskan dan itu harus menjadi objek ALLAH, kan?
Narek
10
Itu hanya pola Singleton yang diterapkan pada level game. Jadi pada dasarnya aspek yang paling menarik adalah memungkinkan Anda berpura-pura tidak menggunakan Singleton. Berguna jika bos Anda berasal dari gereja anti-pola.
GameAlchemist
2
Saya tidak yakin bagaimana ini secara efektif berbeda dari menggunakan Singletons. Bukankah satu-satunya perbedaan dalam seberapa spesifik Anda mengakses layanan tunggal yang ada dari jenis ini? Yaitu var tService = Sys.Game.getService<T>(); tService.Something();alih-alih melewatkan getServicebagian dan mengaksesnya secara langsung?
Christian
1
@Christian: Memang, itulah yang dimaksud dengan antipattern Service Locator. Tampaknya komunitas ini masih menganggapnya sebagai pola desain yang direkomendasikan.
Magus
-1

Unsur penting dari penghapusan lajang yang sering gagal dipertimbangkan oleh lawan lajang adalah menambahkan logika ekstra untuk menangani keadaan yang jauh lebih rumit yang diringkas ketika benda-benda lajang memberi jalan pada nilai-nilai yang instan. Memang, bahkan mendefinisikan keadaan objek setelah mengganti singleton dengan variabel instan bisa sulit, tetapi programmer yang mengganti singleton dengan variabel instan harus siap untuk menghadapinya.

Bandingkan, misalnya, Java HashMapdengan .NET's Dictionary. Keduanya sangat mirip, tetapi mereka memiliki perbedaan utama: Java HashMapadalah hard-coded untuk digunakan equalsdan hashCode(secara efektif menggunakan pembanding singleton) sementara .NET Dictionarydapat menerima contoh IEqualityComparer<T>sebagai parameter konstruktor. Biaya run-time untuk mempertahankannya IEqualityComparerminimal, tetapi kompleksitas yang ditimbulkannya tidak.

Karena setiap Dictionaryinstance mengenkapsulasi a IEqualityComparer, statusnya bukan hanya pemetaan antara kunci yang sebenarnya dikandungnya dan nilai terkaitnya, tetapi juga pemetaan antara set ekivalensi yang ditentukan oleh kunci dan komparator serta nilai terkaitnya. Jika dua contoh d1dan d2dari Dictionaryreferensi terus ke yang sama IEqualityComparer, maka akan mungkin untuk menghasilkan sebuah contoh baru d3sehingga untuk setiap x, d3.ContainsKey(x)akan sama d1.ContainsKey(x) || d2.ContainsKey(x). Namun, jika d1dan d2dapat memiliki referensi ke berbagai pembanding kesetaraan sewenang-wenang, secara umum tidak akan ada cara untuk menggabungkan mereka untuk memastikan bahwa kondisi tersebut berlaku.

Menambahkan variabel instan dalam upaya untuk menghilangkan lajang, tetapi gagal untuk memperhitungkan dengan benar kemungkinan keadaan baru yang dapat diimplikasikan oleh variabel instan tersebut dapat membuat kode yang akhirnya menjadi lebih rapuh daripada seharusnya dengan singleton. Jika setiap instance objek memiliki bidang yang terpisah, tetapi hal-hal akan mengalami kegagalan fungsi yang buruk kecuali mereka semua mengidentifikasi instance objek yang sama, maka semua bidang baru telah dibeli adalah semakin banyak cara kesalahan bisa terjadi.

supercat
sumber
1
Meskipun ini jelas merupakan aspek yang menarik untuk dibahas, saya tidak berpikir itu benar-benar menjawab pertanyaan yang sebenarnya ditanyakan oleh OP.
Josh
1
@JoshPetrie: Komentar pada posting asli (misalnya oleh Magus) menyarankan bahwa biaya runtime untuk membawa referensi untuk menghindari penggunaan lajang tidak besar. Karena itu, saya akan mempertimbangkan relevan biaya semantik dari setiap objek merangkum referensi untuk tujuan menghindari lajang. Seseorang harus menghindari lajang dalam kasus-kasus di mana mereka dapat dihindari dengan murah dan mudah, tetapi kode yang tidak secara terang-terangan membutuhkan lajang, tetapi bisa pecah begitu beberapa kali ada sesuatu yang lebih buruk daripada kode yang menggunakan lajang.
supercat
2
Jawaban bukan untuk mengatasi komentar mereka untuk langsung menjawab pertanyaan.
ClassicThunder
@ClassicThunder: Apakah Anda menyukai hasil edit yang lebih baik?
supercat