26 Kasım 2014 Çarşamba

Gof - Singleton Örüntüsü

Not1 : GoF Tasarım Örüntüleri yazısına bakabilirsiniz.
Not 2: Singleton aynı zamanda Multiton örüntüsü ile kardeştir.

Gang of Four
Tasarım örüntülerinin amaçlarından bir tanesi low coupling, high cohesion'a sahip kodlama yapmak.

High Coupling (Çok Fazla Bağımlılık) Örneği
Örnekte bir sınıf değişince bir başka sınıfın da etkilenmesi gösteriliyor. Bu durum high coupling için tipik bir sonuç.

Low Coupling (Düşük Bağımlılık) Neden Lazım ?
Yazılım zaman içinde değişir. Değiştikçe de birbirlerine bağımlı olan nesneler, katmanlar da değişirler. Her değişikliğin maliyetini azaltmak için bağımlılıkların gevşek olması gerekir. Bu yüzden low coupling'in ilk ve en önemli prensibi arayüzler tanımlayarak bu arayüzleri gerçekleştiren metodlar da değişiklik yapmaktır. Bir diğer yöntem ise Encapsulation kullanmaktır. Encapsulation ile diğer sınıfın içini bilmeden örneğin process() isimli bir metodunu çağırarak çalışırız.

Singleton
Singleton şu özellikleri taşır
  • Accessible via a global, static instance field
  • Created either on program initialization or upon first access
  • No public constructor (cannot instantiate directly)
  • Never explicitly freed (implicitly freed on program termination)
Şu dezavantajlarla gelir
  • Inability to use abstract or interface classes (Eğer doğru kullanılırsa bu madde ortadan kalkabilir)
  • Inability to subclass
  • High coupling across the application (difficult to modify)
  • Difficult to test (can't fake/mock in unit tests)
  • Difficult to parallelize in the case of mutable state (requires extensive locking)

Kullanım Yerleri
Lookup işleri için kullanılabilir.

Singleton ve Arayüz
Örnekte singleton ve arayüzün nasıl kullanılacağı gösteriliyor.
class ServerFactory
{
    public:
        // By default return a RealServer
        ServerInterface& getServer();

        // Set a non default server:
        void setServer(ServerInterface& server);
};

class ServerInterface { /* define Interface */ };

class RealServer: public ServerInterface {}; // This is a singleton (potentially)

class TestServer: public ServerInterface {}; // This need not be.
Bir başka örnekte Singleton preprocessor kullanılarak FileSystem arayüzünün farklı gerçekleştirimlerini dönüyor.

FileSystem& FileSystem::instance()
{
  #if PLATFORM == PLAYSTATION3
    static FileSystem *instance = new PS3FileSystem();
  #elif PLATFORM == WII
    static FileSystem *instance = new WiiFileSystem();
  #endif

  return *instance;
}

Singleton Kullanmamak İçin
Şu yöntemler kullanılabilir.

  • Pass it around
  • Create a context object
  • Put it in base class
  • Dependency Injection veya Service Locator
Context Nesnesi Çözümü
class World
{
public:
  static World& instance() { return instance_; }

  // Functions to set log_, et. al. ...

  Log&         getLog()         { return *log_; }
  FileSystem&  getFileSystem()  { return *fileSystem_; }
  AudioPlayer& getAudioPlayer() { return *audioPlayer_; }

private:
  static World instance_;

  Log         *log_;
  FileSystem  *fileSystem_;
  AudioPlayer *audioPlayer_;
};
Daha sonra context'e şöyle erişiliyor
World::instance().getAudioPlayer().play(VERY_LOUD_BANG);


Base Class Çözümü
Örnek:
class GameObject
{
protected:
  Log& getLog() { return log_; }

private:
  static Log& log_;
};

class Enemy : public GameObject
{
  void doSomething()
  {
    getLog().write("I can log!");
  }
};

C++
Kural 1
Default constructor her zaman private olmalıdır.

Kural 2
"copy constructor" ve "assignment operator" metodlarını private yapmak gerekir. C++11 ile copy constructor'ı silmek için şöyle yapılır.
Singleton(const Singleton&) = delete;
Bunu yapmazsak bizim singleton olduğunu düşündüğümüz bir nesne kopyalanabilir.
Singleton * s = Singleton::getInstance();

Singleton copy = *s;   // <-- invokes copy constructor
veya
Singleton s = Singleton::getInstance ( );
veya
Singleton * s1 = Singleton::getInstance();
Singleton * s2 = Singleton::getInstance();

*s1 = *s2;    // <-- invokes assignment operator
Kural 3
Singleton pointer veya referans olarak döndürülebilir. Ben referans olanını seviyorum.

Yöntem - 1 : Static Sınıf Değişkeni Olarak - Lazy
Bu yöntem klasik olanı. Lazy kullanımda singleton'a ilk erişen kişi nesneyi ilklendirir. Multi-thread uygulamalarda sorun çıkarır.

class Singleton
{
public:
  static Singleton& instance()
  {
    // Lazy initialize.
    if (instance_ == NULL) instance_ = new Singleton();
    return *instance_;
  }

private:
  Singleton() {}

  static Singleton* instance_;
};

Yöntem - 2 :Local Static Değişkeni Olarak
Bu örnekte, singleton nesnesi statik olarak instance() metodu içinde tanımlanıyor. Local static değişkenler multi-threaded bile olsa bir kere yaratıldığı için bu yöntem thread-safe.
class Singleton
{
private:
   Singleton() = default;
   Singleton(const Singleton&) = delete;
public:
   static Singleton& instance() { static Singleton z; return z; }
};
Bir başka örnekte nesne heap'te yaratılyor.
class Singleton
{
public:
  static Singleton& instance()
  {
    static Singleton *instance = new Singleton();
    return *instance;
  }

private:
  Singleton() {} 
};

Yöntem - 3 :Tüm Alanları Static Olarak
Örnek ver

Java
Java Singleton Örnekleri yazısına taşıdım.

C#
Örnek'te bir ata sınıftan kalıtma gösterilmiş:
Ata sınıf:
public abstract class Singleton<ClassType> where ClassType : new()
{
  static Singleton()
  {
  }

  private static readonly ClassType instance = new ClassType();

  public static ClassType Instance
  {
    get
    {
      return instance;
    }
  }
}
Kalıtan sınıf:
class Example : Singleton<Example>
{
  public int ExampleProperty { get; set; }
}
Kullanım:
public void LameExampleMethod()
{
  Example.Instance.ExampleProperty++;
}




Hiç yorum yok:

Yorum Gönder