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.
Görev
KolayBir 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.GameSettingssınıfınız üç ayarı özel (private) üye olarak saklamalıdır:difficulty(string),volume(0-100 arası tam sayı) vefullscreen(boolean). Bunları varsayılan değerlerle başlatın:"Normal",50vefalse.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üncellersetVolume(int vol)— ses seviyesini güncellersetFullscreen(bool fs)— tam ekran modunu güncellerdisplaySettings()— 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
applyGamePresetadlı bir fonksiyon tanımlayın. Bu fonksiyonGameSettingssingleton'ına erişmeli ve onu hazır ayara göre yapılandırmalıdır:"casual"— zorluğu"Easy", ses seviyesini70, tam ekranıfalseolarak ayarlar"competitive"— zorluğu"Hard", ses seviyesini30, tam ekranıtrueolarak 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:
- Bir hazır ayar adı (string)
- Ö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
applyGamePresetfonksiyonunuzu 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: nocompetitive ve 20 girdileriyle:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Hard
Volume: 30
Fullscreen: yes
Difficulty: Hard
Volume: 20
Fullscreen: yesHem 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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner