Singleton'a Alternatif Ne Var?


114

Uygulama için konfigürasyon bilgilerini tutan bir sınıfımız var. Eskiden bir singletondı. Biraz mimari incelemeden sonra, tekliyi kaldırmamız söylendi. Tek seferde farklı konfigürasyonları test edebildiğimiz için birim testinde singleton kullanmamanın bazı faydalarını gördük.

Singleton olmadan, örneği kodumuzun her yerine geçirmeliyiz. O kadar dağınık hale geldi ki, tekli ambalaj yazdık. Şimdi aynı kodu PHP ve .NET'e taşıyoruz, yapılandırma nesnesi için kullanabileceğimiz daha iyi bir model olup olmadığını merak ediyorum.

Yanıtlar:


131

Google Test Blog (test edilebilir kodu oluşturmak için sırayla) Singleton kurtulma konusunda girişleri bir dizi vardır. Belki bu size yardımcı olabilir:

Son makale, yeni nesnelerin oluşturulmasının bir fabrikaya nasıl taşınacağını ayrıntılı olarak açıklıyor, böylece tekil kullanmaktan kaçınabilirsiniz. Kesinlikle okumaya değer.

Kısacası, tüm yeni operatörleri bir fabrikaya taşıyoruz. Benzer kullanım ömrüne sahip tüm nesneleri tek bir fabrikada gruplandırıyoruz.


3
*** Tekilleri önlemek için bağımlılık enjeksiyonu kullanma
Justin

Bu makaleler Google C ++ Programlama Standartları kadar iyidir!

2
Aslında değil. Örneğin, 'Statik yöntemler kullanma' tavsiyesi, Scott Meyers / Herb Sutters'ın minimal arayüz ilkesine aykırıdır. Yararlı tavsiyeler var, ancak çoklu zihnin katkısından yoksundurlar.
Matthieu M.

@FrankS neden bağlantıların sırasını değiştirdiniz? İlk başta iyi bir kronolojik durumdaydı.
cregox

@Cawas aslında hiçbir fikrim yok, bu dört yıldan daha önceydi, o zaman sanırım bunun için bazı nedenlerim vardı :-)
FrankS

15

En iyi yol, bunun yerine bir Fabrika modeli kullanmaktır. Sınıfınızın (fabrikada) yeni bir örneğini oluşturduğunuzda, 'global' verileri yeni oluşturulan nesneye, tek bir örneğe (fabrika sınıfında depoladığınız) referans olarak veya ilgili kopyayı kopyalayarak ekleyebilirsiniz. verileri yeni nesneye.

Tüm nesneleriniz, daha önce tekil içinde yaşamak için kullanılan verileri içerecektir. Genel olarak pek bir fark olduğunu sanmıyorum, ancak kodunuzun okunmasını kolaylaştırabilir.


1
"En iyi yol" ifadesine katılmıyorum, ancak iyi bir alternatif için +1.
tylermac

Bu yaklaşımla ilgili sorun, her yeni nesnenin, potansiyel olarak büyük miktarda veri olabilecek şeyleri içermesidir (veya referanslar). gob içeren nesnelerin herhangi birinde var_dump (), özyineleme uyarıları ile serbestçe serpiştirilmiş devasa listelerde çok hızlı sonuç verir . Çirkinleşiyor, çok verimli olamıyor ve her şeyi çirkin gösteriyor. Ancak kişisel olarak daha iyi bir yol bulamadım. "Factory" yöntemini __construct () kullanarak bir global. Korkunç Singleton'dan kaçınmak için her şey geriye doğru bükülmüş gibi geliyor ...
FYA

2
@EastGhostCom: Tekliyi kullanabilir ve işleri kendimiz için zorlaştırmayı
bırakabiliriz

5

Burada bariz olanı belirtiyor olabilirim, ancak Spring veya Guice gibi bir bağımlılık enjeksiyon çerçevesi kullanamamanızın bir nedeni var mı? (Spring'in artık .NET için de mevcut olduğuna inanıyorum).

Bu şekilde, çerçeve, yapılandırma nesnelerinin tek bir kopyasını tutabilir ve fasulyelerinizin (hizmetler, DAO'lar, her neyse) ona bakma konusunda endişelenmenize gerek kalmaz.

Genelde benimsediğim yaklaşım budur!


4

Eğer kullanırsanız Spring Framework , sadece normal bir fasulye oluşturabilir. Varsayılan olarak (veya açıkça ayarladıysanız scope="singleton"), bean'ın yalnızca bir örneği oluşturulur ve bu örnek, bir bağımlılıkta her kullanıldığında veya aracılığıyla alındığında döndürülür getBean().

Singleton modelinin bağlanması olmadan tek örnek avantajını elde edersiniz.


3
Ah ironi - singleton yerine (singleton) bahar fasulyesi kullan ...
Zack Macomber

4

Alternatif, bir nesne istemek yerine ihtiyacınız olan şeyi iletmektir.


4

hem anlaşılması zor hem de kırılgan olan çok büyük bir nesnede sona ereceği için tek bir konfigürasyon nesnesine karşı sorumluluklar toplamayın .

Örneğin, belirli bir sınıf için başka bir parametreye ihtiyacınız varsa, Configurationnesneyi değiştirirsiniz , ardından onu kullanan tüm sınıfları yeniden derlersiniz. Bu biraz sorunlu.

Ortak, genel ve büyük bir Configurationnesneden kaçınmak için kodunuzu yeniden düzenlemeyi deneyin . İstemci sınıflarına yalnızca gerekli parametreleri iletin:

class Server {

    int port;

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

}

şu şekilde yeniden düzenlenmelidir:

 class Server {

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

bir bağımlılık ekleme çerçevesi burada çok yardımcı olacaktır, ancak kesinlikle gerekli değildir.


Evet bu gerçekten iyi bir nokta. Bunu daha önce yapmıştım. Benim büyük konfigürasyon nesnem MailServiceConf, ServerConf .. gibi arayüzler uygulamaktı, Dependency Injection çerçevesi konfigürasyonu sınıflara geçirdiğinden, sınıflarım büyük Configuration nesnesine bağlı kalmadı.
caltuntas

1

Statik yöntemler kullanarak aynı tekli davranışı gerçekleştirebilirsiniz. Steve Yegge çok iyi açıklıyor bu yazı.


Aslında makale oldukça iyi ve bunun yerine statik yöntemler kullanmanız gerektiğini söylemiyor. Bunun yerine, statik yöntemlerin de sadece tek tonlar olduğunu belirtir ve sonunda fabrika yöntemi modelini kullanmanızı önerir: "Hala Singleton nesnelerini kullanma ihtiyacı hissediyorsanız, bunun yerine Fabrika Yöntemi modelini kullanmayı düşünün. ... "
FrankS

0

Yalnızca statik yöntemler ve alanlar içeren bir sınıf mümkün müdür? Durumunuzun tam olarak ne olduğundan emin değilim, ama araştırmaya değer olabilir.


1
Sınıf durumsuz ise, statik bir sınıf olmalıdır.
AlbertoPL

1
C ++ 'dadır - model bir Monostat olarak bilinir.

0

Hangi aletlerin / çerçevelerin vb. Kullanıldığına bağlıdır. Bağımlılık enjeksiyonu / ioc araçlarıyla, di / ioc konteynerinin gerekli sınıf için tekli davranış (IConfigSettings arabirimi gibi) kullanmasına sahip olarak (örneğin, bir IConfigSettings arabirimi) sınıfın yalnızca bir örneğini oluşturarak tekil performans / optimizasyonlar elde edilebilir. Bu hala test için değiştirilebilir

Alternatif olarak, sınıf oluşturmak ve her talep ettiğinizde aynı örneği döndürmek için bir fabrika da kullanılabilir - ancak test etmek için stubbed / alay edilmiş bir sürüm döndürebilir.


0

Geri arama arayüzü olarak yapılandırma yapma olasılığını gözden geçirin. Böylece yapılandırmaya duyarlı kodunuz şöyle görünecektir:

MyReuseCode.Configure(IConfiguration)

Sistem başlatma kodu şöyle görünecektir:

Library.init(MyIConfigurationImpl)

0

Yapılandırma nesnesine geçmenin acısını hafifletmek için bir bağımlılık enjeksiyon çerçevesi kullanabilirsiniz. İyi bir tanesidir Ninject xml yerine kod kullanarak avantajına sahip olan.


0

Belki de çok temiz olmayabilir, ancak değiştirmek istediğiniz bilgi bitlerini tekli tonu oluşturan yönteme geçirebilirsiniz - kullanmak yerine

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

Eğer diyebiliriz createSingleton(Information info)uygulama başlatma (ve birim testlerinin Kur-Yöntemler) doğrudan.


-1

Tekil sesler kötü değildir, ancak tasarım modeli kusurludur. Çalışma zamanı sırasında yalnızca tek bir örneğini oluşturmak istediğim ancak deterministik sonuçlar sağlamak için birim testi sırasında birden çok yalıtılmış örnek oluşturmak istediğim bir sınıfa sahibim.

Yay vb. Kullanarak DI çok iyi bir seçenektir ancak tek seçenek değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.