Menu
Coddy logo textTech

Singleton Kalıbı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 91 / 104.

Singleton deseni, bir sınıfın tüm program boyunca yalnızca bir örneğe sahip olmasını sağlar ve ona küresel bir erişim noktası sunar. Bu, yapılandırma yöneticisi, günlükçü (logger) veya veritabanı bağlantı havuzu gibi paylaşılması gereken kaynaklar için kullanışlıdır.

Temel fikir, hiç kimsenin doğrudan örnek oluşturamaması için yapıcıyı (constructor) özel (private) hale getirmek ve ardından tek örneği döndüren statik bir yöntem sağlamaktır:

#include <iostream>
#include <string>

class Logger {
private:
    std::string logFile;
    
    // Private constructor - prevents direct instantiation
    Logger() : logFile("app.log") {}
    
    // Delete copy constructor and assignment operator
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Static method to access the single instance
    static Logger& getInstance() {
        static Logger instance;  // Created once, lives until program ends
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

int main() {
    Logger::getInstance().log("Application started");
    
    Logger& logger = Logger::getInstance();
    logger.log("Processing data");
}

getInstance() içindeki static Logger instance, "magic statics" (sihirli statikler) olarak adlandırılan bir C++11 özelliğidir; iş parçacığı güvenlidir (thread-safe) ve yalnızca bir kez başlatılması garanti edilir. Kopya kurucuyu ve atama operatörünü silerek, herhangi birinin singleton nesnemizin kopyalarını oluşturmasını engelleriz.

Singleton'ı idareli kullanın - test etmeyi zorlaştırabilen küresel bir durum (global state) sunar. Birden fazla örneğe sahip olmanın sorun yaratacağı, gerçekten paylaşılan kaynaklar için en uygunudur.

challenge icon

Görev

Kolay

Bir oyun uygulaması için yapılandırmayı yöneten bir GameSettings singleton yapısı oluşturalım. Bu, Singleton deseni için mükemmel bir kullanım durumudur; oyununuzun tüm bölümlerinin erişebileceği, uygulama genelinde tutarlı bir yapılandırma sağlayan tam olarak bir adet ayar yöneticisi istersiniz.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • GameSettings.h: Oyun yapılandırmasını yöneten singleton sınıfınızı tanımlayın.

    GameSettings sınıfınız üç ayarı özel (private) üye olarak saklamalıdır: difficulty (string), volume (0-100 arası tam sayı) ve fullscreen (boolean). Bunları varsayılan değerlerle başlatın: "Normal", 50 ve false.

    Singleton desenini düzgün bir şekilde uygulayın:

    • Yapıcıyı (constructor) private yapın
    • Kopya yapıcıyı (copy constructor) ve atama operatörünü (assignment operator) silin
    • Tek örneğe bir referans döndüren statik bir getInstance() metodu sağlayın

    Ayarlarla etkileşim kurmak için şu genel (public) metodları ekleyin:

    • setDifficulty(const std::string& diff) — zorluğu günceller
    • setVolume(int vol) — ses seviyesini günceller
    • setFullscreen(bool fs) — tam ekran modunu günceller
    • displaySettings() — tüm mevcut ayarları yazdırır
  • GameModule.h: Singleton'a erişen ayrı bir modül oluşturun.

    Bir hazır ayar adı (string) alan applyGamePreset adlı bir fonksiyon tanımlayın. Bu fonksiyon GameSettings singleton'ına erişmeli ve onu hazır ayara göre yapılandırmalıdır:

    • "casual" — zorluğu "Easy", ses seviyesini 70, tam ekranı false olarak ayarlar
    • "competitive" — zorluğu "Hard", ses seviyesini 30, tam ekranı true olarak ayarlar
    • Diğer tüm hazır ayarlar — varsayılan ayarları değiştirmeden tutar

    Bu, bir programın farklı bölümlerinin aynı singleton örneğine nasıl erişebileceğini ve onu nasıl değiştirebileceğini gösterir.

  • main.cpp: Singleton'ı iş başında göstermek için her şeyi bir araya getirin.

    İki girdi okuyun:

    1. Bir hazır ayar adı (string)
    2. Özel bir ses seviyesi (tam sayı)

    Öncelikle, singleton üzerinde displaySettings() metodunu çağırarak başlangıçtaki varsayılan ayarları görüntüleyin. Ardından boş bir satır yazdırın.

    Sonra applyGamePreset fonksiyonunuzu kullanarak hazır ayarı uygulayın ve ayarları tekrar görüntüleyin. Ardından boş bir satır yazdırın.

    Son olarak, sadece ses seviyesini özel girdi değeriyle geçersiz kılın ve nihai ayarları görüntüleyin.

displaySettings() metodu tam olarak şu formatta çıktı vermelidir:

Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]

Örneğin, casual ve 85 girdileriyle:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Easy
Volume: 70
Fullscreen: no

Difficulty: Easy
Volume: 85
Fullscreen: no

competitive ve 20 girdileriyle:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Hard
Volume: 30
Fullscreen: yes

Difficulty: Hard
Volume: 20
Fullscreen: yes

Hem main.cpp hem de GameModule.h dosyalarının aynı singleton örneğine nasıl eriştiğine dikkat edin; bir yerde yapılan değişiklikler her yerde görünür. Oyun ayarları gibi paylaşılan kaynaklar için Singleton deseninin gücü budur.

Kopya kağıdı

Singleton deseni, bir sınıfın program boyunca yalnızca bir örneğe sahip olmasını sağlar ve ona küresel erişim sunar. Yapılandırma yöneticileri, günlükçüler (loggers) veya veritabanı bağlantı havuzları gibi paylaşılan kaynaklar için kullanışlıdır.

Temel uygulama adımları:

  • Doğrudan örneklemeyi önlemek için yapıcıyı (constructor) özel (private) yapın
  • Kopyalamayı önlemek için kopya yapıcıyı ve atama operatörünü silin
  • Tek örneğe bir referans döndüren statik bir yöntem sağlayın
class Logger {
private:
    std::string logFile;
    
    // Özel yapıcı
    Logger() : logFile("app.log") {}
    
    // Kopya yapıcıyı ve atama operatörünü sil
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Tek örneğe erişmek için statik yöntem
    static Logger& getInstance() {
        static Logger instance;  // Bir kez oluşturulur, program bitene kadar yaşar
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

// Kullanım
Logger::getInstance().log("Application started");

Logger& logger = Logger::getInstance();
logger.log("Processing data");

getInstance() içindeki static Logger instance, C++11 "magic statics" (sihirli statikler) özelliğini kullanır; bu özellik iş parçacığı açısından güvenlidir (thread-safe) ve yalnızca bir kez başlatılması garanti edilir.

Singleton'ı idareli kullanın çünkü test etmeyi zorlaştırabilecek küresel bir durum (global state) sunar. Birden fazla örneğin sorun yaratacağı, gerçekten paylaşılan kaynaklar için en uygunudur.

Kendin dene

#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"

using namespace std;

int main() {
    // Girdileri oku
    string presetName;
    int customVolume;
    cin >> presetName;
    cin >> customVolume;
    
    // TODO: GameSettings singleton örneğini al
    
    // TODO: Başlangıç varsayılan ayarlarını görüntüle
    
    // TODO: Boş bir satır yazdır
    
    // TODO: applyGamePreset fonksiyonunu kullanarak ön ayarı uygula
    
    // TODO: Ön ayar uygulandıktan sonraki ayarları görüntüle
    
    // TODO: Boş bir satır yazdır
    
    // TODO: Ses seviyesini customVolume ile geçersiz kıl
    
    // TODO: Son ayarları görüntüle
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler