Doğru Singleton<T> Tasarımı

oğuzhan katlı
2 min readOct 10, 2019

--

En az eforla istenilen bir sınıfı Singleton yapmak için genel(generic) bir Singleton tasarımı kullanmak istediğimizde, internette karşımıza çıkan en yaygın tasarım ve uygulaması aşağıdaki gibidir:

template <typename T>
class Singleton {
protected:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
virtual ~Singleton() = default;
public:
static T& getInstance()
{
static T instance;
return instance;
}
};
// Singleton yapılmak istenilen sınıf
class ClassA : public Singleton<ClassA> {
private:
friend class Singleton<ClassA>;
MySingletonClassA() = default;
public:
void foo() {}
void bar() {}
};
// Singleton nesnenin kullanımı
ClassA::getInstance()->foo();

Ancak bu tasarım ile Singleton yapılan sınıflar için bazı tasarımsal kısıtlamalar bulunmaktadır. Genel Singleton tasarımın sahip olduğunu kısıtlamalar, farklı projelerde veya farklı gereksinimlere sahip olan sistemlerde tasarımın kullanılamaz hale gelmesine yol açabilmektedirler; birkaç örnek vermek gerekirse:

  • her sınıf varsayılan kurucu fonksiyon(default constructor) ile yaratılma imkanına sahip olmayabilir; özelleştirilmiş bir kurucu fonksiyonu olan bir sınıfın bu tasarım ile Singleton olarak kullanılması imkansız olacaktır
  • Bazı projelerde çoklu kalıtıma izin verilmeyebilir; böyle bir durumda Singleton<T> sınıfından kalıtım mümkün olmayacağı için, bu tasarımın kullanılması imkansız olacaktır
  • Qt gibi araçlarda sınıfların varsayılan kurucu fonksiyonları dışarıya açık olmalıdır; Singleton<T> sınıfının bu fonksiyonları gizli olarak işaretlenmesi, bu sistemlerde tasarımın kullanılmasını imkansız hale getirmektedir
  • Aynı şekilde bir sınıf istenildiği zaman Singleton veya normal olarak kullanılmak istenebilir. Kalıtımın kullanılması bu imkanı da yok etmektedir

Peki bu kısıtlamaları ortadan kaldıran ve mümkün olan en az eforla kullanılabilecek bir Singleton tasarımı nasıl olmalıdır?

Yeni tasarımın, eski tasarımda gözlemlenen problemleri çözmesi için, bazı ek gereksinimlere sahip olması gerekmektedir. Bu gereksinimler:

  • Bir sınıf kalıtım kullanılmadan Singleton yapılabilmeli
  • Sınıflar Singleton veya normal olarak kullanılabilmeli
  • Özelleştirilmiş kurucu fonksiyona sahip sınıflar da Singleton olarak kullanılabilmeli
  • Sınıfların kurucu fonksiyonları ve erişim hakları ile ilgili bir kısıtlama olmamalı

Yukarıdaki özelliklere sahip bir Singleton tasarımı mutlaka şablon(template) bir yapıda olmalıdır. Eski tasarımdaki gibi şablon bir yapıya sahip, ancak kalıtım kullanmayan bir yapıda olması gereken yeni tasarımın kullanımı aşağıdaki gibi olmalıdır:

// Singleton olarak kullanılmak istenen sınıf:
class ClassA : public BaseClass {
public:
ClassA(const char *name) : BaseClass(name) {}
...
};
// ClassA sınıfını normal bir şekilde kullanmak için
std::unique_ptr<BaseClass> a(new ClassA("ClassA"));
a->foo();
// ClassA sınıfını singleton olarak kullanmak için
Singleton<ClassA> a("ClassA");
a->foo();
// veya
Singleton<ClassA>::getInstance()->foo();

Yeni Singleton Tasarımı

singleton.hpp

--

--