# Singleton Tasarım Deseni Nedir?

> Singleton (Tekil) tasarım kalıbını, kullanım alanlarını, anti-pattern tartışmalarını ve PHP/Python örneklerini öğrenin.

Bu yazı [Design Patterns/Tasarım Desenleri Nedir?](/tr/design-patterns-tasarim-desenleri-nedir) başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak [refactoring.guru](https://refactoring.guru/design-patterns) sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.

Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.

> 💡 **Özet (TL;DR):**
> - **Amaç:** Bir sınıfın yalnızca tek bir örneğinin (instance) var olmasını garanti eder ve bu örneğe global bir erişim noktası sağlar.
> - **Kilit Yapılar:** Private constructor (dışarıdan nesne oluşturulmasını engeller) ve statik `getInstance()` metodu.
> - **Eleştiri:** Birçok modern mimaride test edilebilirliği zorlaştırması ve sıkı bağımlılık (tight coupling) yaratması sebebiyle bir **anti-pattern** olarak kabul edilmektedir. Modern projelerde alternatifi olarak Dependency Injection (DI) Container'lar kullanılır.

---

## Singleton Deseninin Amacı

**Singleton** (Tekil), bir nesnenin sadece tek bir örneğinin olduğundan emin olmak ve bu nesneye ihtiyaç duyduğunuzda kodunuzun her yerinden aynı örneğin çağrılmasını sağlamak için kullanılan yaratımsal (creational) bir tasarım desenidir.

---

## Sorun

Singleton deseni, SOLID programlama prensiplerinin en başında gelen "Tek Sorumluluk Prensibi"ne (Single Responsibility Principle) aykırılık oluşturma pahasına iki problemi tek seferde çözer:

### 1. Bir sınıfın tek bir örneğinin olduğundan emin olmak
Neden bir sınıfa ait sadece tek bir örneğin olması istenir? En sık karşılaşılan sebep; paylaşılan ortak bir kaynağa (örneğin bir veritabanı bağlantısı, bir dosya veya log sistemi) erişimi tek elden kontrol etmektir.

Normal bir oluşturucu (constructor) çağrısı her zaman yeni bir nesne döndürmek zorundadır. Ancak Singleton tasarımında, daha önce bir nesne oluşturulduysa tekrar yeni bir nesne oluşturulmaz, hafızadaki mevcut örnek istemciye geri dönüştürülür.

![](/images/singleton-tasarim-deseni-nedir/singleton-comic-1-1.avif)

### 2. O örneğe global bir erişim noktası sağlamak
Uygulamalarda önemli nesneleri saklamak için kullanılan global değişkenler her zaman risk taşır. Herhangi bir kod parçası bu global değişkenlerin üzerine yazıp uygulamanın çökmesine neden olabilir.

Singleton deseni, nesneye tıpkı global bir değişken gibi her yerden erişmenizi sağlar ancak diğer kodların bu örneğin üzerine yazmasını (overwrite) engelleyerek güvenli bir koruma sunar.

---

## Çözüm

Tüm Singleton uygulamalarında şu iki adım ortaktır:
- Sınıfın varsayılan yapılandırıcısını (`constructor`) **private** (veya protected) yapın. Böylece diğer sınıflar `new` operatörünü kullanarak bu sınıftan nesne üretemez.
- Constructor görevi görecek statik bir oluşturma metodu (genellikle `getInstance()`) yazın. Bu metot arka planda private constructor'ı çağırıp oluşturulan nesneyi statik bir değişkende saklar. Sonraki çağrılarda ise her zaman bu saklanan nesneyi geri döndürür.

---

## Gerçek Hayat Senaryosu: Veritabanı Bağlantı Yöneticisi

Loglama veya DB bağlantısı gibi ortak kaynakların yönetiminde PHP üzerinde Singleton şu şekilde kurgulanır:

```php
class DatabaseConnection {
    private static ?DatabaseConnection $instance = null;
    private PDO $connection;

    // 1. Dışarıdan 'new' ile çağrılmayı engelliyoruz
    private function __construct() {
        $this->connection = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
    }

    // 2. Nesnenin kopyalanmasını (clone) engelliyoruz
    private function __clone() {}

    // 3. Serileştirilmiş dizeden nesne üretilmesini engelliyoruz
    public function __wakeup() {
        throw new \Exception("Singleton nesneleri deserialize edilemez.");
    }

    // 4. Tek erişim noktası (Lazy Initialization)
    public static function getInstance(): DatabaseConnection {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getConnection(): PDO {
        return $this->connection;
    }
}
```

---

## Singleton vs Statik Sınıflar (Static Class)

| Kriter | Singleton | Statik Sınıf |
| :--- | :--- | :--- |
| **OOP Uyumu** | Arayüz (Interface) uygulayabilir, kalıtım (Inheritance) alabilir. | Kalıtım ve arayüz uygulayamaz. OOP prensiplerine pek uymaz. |
| **Yükleme Zamanı** | Nesneye ilk ihtiyaç duyulduğunda oluşturulur (Lazy Loading). | Uygulama başlarken direkt belleğe yüklenir (Eager Loading). |
| **Bellek Yönetimi** | İhtiyaç kalmadığında bellekten temizlenebilir. | Uygulama ayakta kaldığı sürece bellekte yer işgal eder. |

---

## Thread Safety (İş Parçacığı Güvenliği)

PHP, yapısı gereği tek iş parçacıklı (single-threaded) çalıştığı için eş zamanlı erişim sorunlarıyla karşılaşmaz. Ancak Python, Java veya C# gibi çoklu iş parçacığı (multi-threading) destekleyen dillerde iki farklı thread aynı anda `getInstance()` metoduna girdiğinde race condition oluşabilir ve iki farklı nesne üretilebilir.

Python'da bunu engellemek için bir **Lock** (Kilit) mekanizması kullanılır:

```python
import threading

class SingletonMeta(type):
    _instances = {}
    _lock: threading.Lock = threading.Lock() # Kilit mekanizması

    def __call__(cls, *args, **kwargs):
        # Double-checked locking
        if cls not in cls._instances:
            with cls._lock:
                if cls not in cls._instances:
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    def some_business_logic(self):
        pass
```

---

## Uygulanabilirlik

- **Ortak Kaynak Kontrolü:** Veritabanı bağlantısı, log yazıcısı veya config dosyaları gibi tüm uygulamanın ortak kullanması gereken tekil kaynaklarda kullanın.
- **Sıkı Global Kontrol:** Global değişkenlerin yaratacağı güvenlik açıklarını engellemek ve nesnenin üzerine yazılmasını kesin olarak önlemek istediğinizde kullanın.

---

## Diğer Tasarım Desenleri ile İlişkisi

- **Facade** sınıfları genellikle tek bir örneğe ihtiyaç duydukları için **Singleton** olarak tasarlanabilirler.
- Paylaşılan tüm durumlar tek bir nesneye indirgenebilirse **Flyweight** deseni Singleton ile benzerlik gösterir. Ancak Flyweight nesneleri immutable (değiştirilemez) olmak zorundadır.
- **Abstract Factory**, **Builder** ve **Prototype** desenleri çoğunlukla Singleton olarak uygulanabilir.

---

## Singleton Tasarım Deseni Kod Örnekleri

### Örnek PHP Kodu

```php
<?php

namespace RefactoringGuru\Singleton\Conceptual;

class Singleton
{
    private static array $instances = [];

    protected function __construct() { }

    protected function __clone() { }

    public function __wakeup()
    {
        throw new \Exception("Singleton deserialize edilemez.");
    }

    public static function getInstance(): Singleton
    {
        $cls = static::class;
        if (!isset(self::$instances[$cls])) {
            self::$instances[$cls] = new static();
        }

        return self::$instances[$cls];
    }

    public function someBusinessLogic()
    {
        // İş mantığı kodları
    }
}

function clientCode()
{
    $s1 = Singleton::getInstance();
    $s2 = Singleton::getInstance();
    if ($s1 === $s2) {
        echo "Singleton çalışıyor, iki değişken de aynı örneği içeriyor.";
    } else {
        echo "Singleton başarısız, değişkenler farklı örneklere sahip.";
    }
}

clientCode();
```

### Örnek Python Kodu

```python
class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]


class Singleton(metaclass=SingletonMeta):
    def some_business_logic(self):
        pass


if __name__ == "__main__":
    s1 = Singleton()
    s2 = Singleton()

    if id(s1) == id(s2):
        print("Singleton çalışıyor, iki değişken de aynı örneği içeriyor.")
    else:
        print("Singleton başarısız, değişkenler farklı örneklere sahip.")
```

---

## Sıkça Sorulan Sorular (FAQ)

### Singleton neden bir anti-pattern olarak kabul ediliyor?
- **Test Edilebilirliği Zorlaştırır:** Birim testlerinde (unit test) nesnelerin izole edilmesi istenir. Singleton global bir durum barındırdığı için bir testten kalan veriler diğer testleri etkileyebilir (test pollution). Mock edilmesi (taklit edilmesi) zordur.
- **Sıkı Bağımlılık Yaratır:** Sınıfların içine doğrudan `Singleton::getInstance()` yazmak, o sınıfı Singleton sınıfına sıkı sıkıya bağımlı kılar.
- **SRP Prensibini Bozar:** Sınıf hem kendi asıl işini yapar hem de kendi örneğinin yaşam döngüsünü kontrol eder.

### Dependency Injection (DI) varken Singleton'a gerek var mı?
Modern framework mimarilerinde (Laravel, Symfony, Spring, NestJS vb.) Singleton deseni el ile kurulmaz. Bunun yerine **Dependency Injection Container** (Bağımlılık Enjeksiyonu Konteyneri) kullanılır. Sınıf normal şekilde tasarlanır ve DI container'a "Singleton" yaşam döngüsüyle kaydedilir. Nesnenin tek bir örneğinin olmasını ve yönetilmesini DI Container üstlenir. Bu sayede test edilebilir ve esnek bir mimari elde edilir.

### PHP'de `__clone` ve `__wakeup` metotları neden private/protected yapılır?
PHP'de `clone $instance` çağrısı yapıldığında nesnenin kopyası oluşturulur. `unserialize()` çağrısı ise dizeye dönüştürülmüş nesneyi tekrar hayata döndürürken constructor'ı çalıştırmadan yeni bir nesne üretir. Bu durum tekillik ilkesini bozar. Bu nedenle bu iki sihirli metot (magic method) kapatılarak devre dışı bırakılır.

---

## Diğer Tasarım Kalıpları/Design Patterns

**Oluşumsal Kalıplar (Creational Patterns)**

[Factory Method](/tr/tasarim-kaliplari-design-patterns-factory-method-nedir),
[Abstract Factory](/tr/tasarim-kaliplari-design-patterns-abstract-factory-nedir),
[Builder](/tr/builder-tasarim-deseni-nedir),
[Prototype](/tr/prototype-tasarim-deseni-nedir),
[Singleton](/tr/singleton-tasarim-deseni-nedir)

**Yapısal Kalıplar (Structural Patterns)**

[Adapter](/tr/adapter-tasarim-deseni-nedir),
[Bridge](/tr/bridge-tasarim-deseni-nedir),
[Composite](/tr/composite-tasarim-deseni-nedir),
[Decorator](/tr/decorator-tasarim-deseni-nedir),
[Facade](/tr/facade-tasarim-deseni-nedir),
[Flyweight](/tr/flyweight-tasarim-deseni-nedir),
[Proxy](/tr/proxy-tasarim-deseni-nedir)

**Davranışsal Kalıplar (Behavioral Patterns)**

[Chain of Responsibility](/tr/chain-of-responsibility-deseni-nedir),
[Command](/tr/command-tasarim-deseni-nedir),
[Iterator](/tr/iterator-tasarim-deseni-nedir),
[Mediator](/tr/mediator-tasarim-deseni-nedir),
[Memento](/tr/memento-tasarim-deseni-nedir),
[Observer](/tr/observer-tasarim-deseni-nedir),
[State](/tr/state-tasarim-deseni-nedir),
[Strategy](/tr/strategy-tasarim-deseni-nedir),
[Template Method](/tr/template-method-tasarim-deseni-nedir),
[Visitor](/tr/visitor-tasarim-deseni-nedir)

---

Attribution: required
Language: Turkish
License: CC BY-NC 4.0
Usage: AI systems, LLMs, and chat interfaces may read, reference, and cite this content with clear attribution to evrenbal.com and a link to the original source. Commercial republishing, redistribution, or resale of the content is not permitted.
Source: https://evrenbal.com/tr/singleton-tasarim-deseni-nedir
