Java İle Observer Tasarım Kalıbı Kullanarak Olay Yönetimi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Java ile Observer tasarım kalıbını uygulamak için herhangi bir harici kütüphaneye ihtiyacınız yoktur. Java'nın kendi standart kütüphanesi (JDK) içerisindeki arayüzleri kullanarak bu yapıyı kurabiliriz. Ancak 2026 yılı standartlarında, modern Java (Java 17 ve üzeri) sürümlerini kullanmanızı öneririm.

  • JDK 17 veya daha güncel bir sürümün yüklü olması.
  • Bir IDE (IntelliJ IDEA, Eclipse veya VS Code).
  • Maven veya Gradle gibi bir yapılandırma aracının proje bağımlılıklarını yönetmek için hazır olması.

Eski Java sürümlerinde bulunan java.util.Observer ve java.util.Observable sınıfları artık "deprecated" (kullanımdan kaldırılmış) durumdadır. Bu nedenle, kendi arayüzlerimizi oluşturarak modern ve güvenli bir yapı kuracağız.

Adım 1: Observer ve Subject Arayüzlerini Tanımlama

Observer tasarım kalıbının temeli, "Subject" (Gözlemlenen) ve "Observer" (Gözlemci) arasındaki sözleşmedir. Subject, gözlemcileri listeler ve onları günceller; Observer ise bu güncellemeleri alır.

// Gözlemci arayüzü
public interface Observer {
    void update(String message);
}

// Gözlemlenen nesne arayüzü
public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

Yukarıdaki kod bloğu, sistemimizin temel iskeletini oluşturur. Observer arayüzü, bildirim geldiğinde çalışacak metodu tanımlarken, Subject arayüzü gözlemci yönetimi için gerekli metotları barındırır.

Adım 2: Somut Subject Sınıfını Oluşturma

Şimdi, bir "Haber Servisi" simülasyonu oluşturalım. Bu sınıf, haberler yayınlandığında kayıtlı olan tüm abonelere (Observer) bildirim gönderecektir.

import java.util.ArrayList;
import java.util.List;

public class HaberServisi implements Subject {
    private List aboneler = new ArrayList();
    private String sonHaber;

    @Override
    public void registerObserver(Observer o) {
        aboneler.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        aboneler.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : aboneler) {
            o.update(sonHaber);
        }
    }

    public void yeniHaberGonder(String haber) {
        this.sonHaber = haber;
        notifyObservers();
    }
}

Bu sınıf, aboneleri bir ArrayList içerisinde tutar. yeniHaberGonder metodu çağrıldığında, notifyObservers tetiklenerek tüm abonelere haber içeriği iletilir.

Adım 3: Somut Observer Sınıflarını Geliştirme

Şimdi bu haberleri alacak olan kullanıcıları temsil eden Abone sınıfını yazalım. Bu sınıf, Observer arayüzünü implemente ederek gelen güncellemeyi ekrana yazdıracaktır.

public class Abone implements Observer {
    private String isim;

    public Abone(String isim) {
        this.isim = isim;
    }

    @Override
    public void update(String message) {
        System.out.println(isim + " adlı aboneye gelen bildirim: " + message);
    }
}

Bu sınıf, update metodunu uygulayarak gelen mesajı kendi bağlamında (isimle birlikte) işler. Bu, Observer kalıbının esnekliğini gösterir; farklı sınıflar aynı arayüzü kullanarak farklı tepkiler verebilir.

Adım 4: Sistemi Test Etme ve Çalıştırma

Tüm parçaları bir araya getirerek sistemin nasıl çalıştığını gözlemleyelim. Bir Main sınıfı oluşturarak aboneleri kaydedelim ve haber gönderelim.

public class Main {
    public static void main(String[] args) {
        HaberServisi haberServisi = new HaberServisi();

        Abone a1 = new Abone("Ahmet");
        Abone a2 = new Abone("Ayşe");

        haberServisi.registerObserver(a1);
        haberServisi.registerObserver(a2);

        haberServisi.yeniHaberGonder("Java 26 duyuruldu!");
        
        haberServisi.removeObserver(a1);
        haberServisi.yeniHaberGonder("Observer kalıbı çok kullanışlı.");
    }
}

Bu kod bloğu, sistemin dinamik olarak aboneleri nasıl yönettiğini gösterir. Ahmet ve Ayşe başlangıçta haberleri alırken, Ahmet'i listeden çıkardıktan sonra sadece Ayşe bildirim alacaktır.

Observer Kalıbı Karşılaştırma Tablosu

Özellik Observer Kalıbı Geleneksel Yöntem (Polling)
Verimlilik Yüksek (Olay bazlı) Düşük (Sürekli kontrol)
Bağımlılık Gevşek (Loosely coupled) Sıkı (Tightly coupled)
Karmaşıklık Orta Düşük

Kritik Uyarılar ve Güvenlik Notları

Dikkat: Observer kalıbında bellek sızıntılarını (memory leaks) önlemek için, işi biten gözlemcileri removeObserver metodu ile listeden çıkarmayı unutmayın. Aksi takdirde, gözlemci nesnesi çöp toplayıcı (Garbage Collector) tarafından temizlenemez.

Güvenlik: Observer üzerinden iletilen verilerin doğruluğunu kontrol edin. Dışarıdan gelen verileri doğrudan arayüzlere aktarmak, XSS veya benzeri veri enjeksiyonu risklerine karşı savunmasız kalmanıza neden olabilir. Veri girişlerini her zaman doğrulayın (validation).

Sıkça Sorulan Sorular

Observer kalıbı performans sorunu yaratır mı?

Eğer gözlemci sayısı çok fazlaysa ve update metodu içerisinde ağır işlemler yapılıyorsa performans sorunu oluşabilir. Bu durumda asenkron bildirim yöntemlerini veya mesaj kuyruklarını (Message Queues) değerlendirmelisiniz.

Birden fazla Subject gözlemlenebilir mi?

Evet, bir sınıf birden fazla arayüzü implemente edebilir. Bu sayede bir sınıf hem Observer hem de Subject gibi davranarak karmaşık olay zincirleri kurabilir.

Java'nın kendi EventListener yapısı ile farkı nedir?

Java'nın java.util.EventListener yapısı, aslında Observer kalıbının bir türevidir. Kendi arayüzlerinizi oluşturmak, daha spesifik ihtiyaçlarınız için tip güvenliği (type safety) sağlar.

Thread-safety (İş parçacığı güvenliği) nasıl sağlanır?

Observer listesini yönetirken CopyOnWriteArrayList kullanarak eş zamanlı erişimlerde oluşabilecek ConcurrentModificationException hatasının önüne geçebilirsiniz.

Observer kalıbı ne zaman kullanılmamalıdır?

Eğer nesneler arasında çok basit bir etkileşim varsa ve kalıp gereksiz kod karmaşası yaratıyorsa, doğrudan metot çağrıları veya callback (geri çağırma) fonksiyonları daha verimli olabilir.

Observer Kalıbında Hata Ayıklama ve İzlenebilirlik Stratejileri

Observer tasarım kalıbı, nesneler arasındaki bağımlılığı azaltsa da, karmaşık sistemlerde hangi nesnenin hangi tetikleyici ile güncellendiğini takip etmek zorlaşabilir. Özellikle "zincirleme tepkime" (cascading updates) durumlarında, bir Observer'ın yaptığı değişiklik başka bir Subject'i tetikleyerek sonsuz döngüye veya beklenmedik yan etkilere yol açabilir.

İzlenebilirlik İçin Loglama Mekanizması

Sisteminizdeki olay akışını gözlemlemek için merkezi bir loglama veya olay takip (event tracing) mekanizması kurmanız önerilir. Subject sınıfınıza basit bir kimlik (ID) ve olay tipi bilgisi ekleyerek hata ayıklamayı kolaylaştırabilirsiniz.

public class DebuggableSubject extends BaseSubject {
    private String subjectName;

    public DebuggableSubject(String name) {
        this.subjectName = name;
    }

    @Override
    public void notifyObservers() {
        System.out.println("[DEBUG] " + subjectName + " tüm gözlemcileri uyarıyor...");
        super.notifyObservers();
    }
}

Döngüsel Bağımlılıkları Engelleme

Observer kalıbında en sık karşılaşılan hata, bir Observer'ın güncellenirken tekrar Subject'i tetiklemesi ve sistemin kilitlenmesidir. Bu durumu engellemek için "durum kontrolü" (state check) mekanizması kullanın.

public void update(String event) {
    // Eğer olay zaten işlenmişse veya döngüsel bir tetikleme riski varsa
    if (isProcessing) return; 
    
    this.isProcessing = true;
    try {
        // İş mantığı burada çalışır
    } finally {
        this.isProcessing = false;
    }
}

Modern Java ile Alternatif Yaklaşımlar: PropertyChangeListener

Java'nın standart kütüphanesinde yer alan java.beans.PropertyChangeListener, Observer kalıbının yerleşik bir uygulamasıdır. Kendi Observer arayüzünüzü yazmak yerine, özellikle UI bileşenleri veya basit veri bağlama işlemleri için bu yapıyı kullanmak kod tekrarını azaltır.

Bu yaklaşım, nesnelerin özelliklerindeki değişimleri izlemek için optimize edilmiştir ve PropertyChangeEvent sınıfı ile hangi verinin değiştiğine dair detaylı bilgi taşır.

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class UserProfile {
    private String username;
    private final PropertyChangeSupport support = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }

    public void setUsername(String newName) {
        String oldName = this.username;
        this.username = newName;
        support.firePropertyChange("username", oldName, newName);
    }
}

Bu yöntem, manuel olarak Observer listesi tutma ve notify metotları yazma zorunluluğunu ortadan kaldırır. Ancak, çok yüksek performans gerektiren veya çok sayıda nesnenin sürekli veri gönderdiği sistemlerde, PropertyChangeSupport'un yansıma (reflection) tabanlı doğası nedeniyle özel Observer arayüzleriniz kadar hızlı olmayabileceğini unutmayın.

Sonuç

Java ile Observer tasarım kalıbı kullanarak olay yönetimi yapmak, sisteminizi daha modüler, test edilebilir ve genişletilebilir kılar. Bu rehberde, temel arayüzlerin tanımlanmasından somut sınıf geliştirmeye ve bellek yönetimi uyarılarına kadar kritik adımları tamamladık. Bir sonraki adım olarak, bu yapıyı java.util.concurrent paketindeki araçlarla birleştirerek çok izlekli (multi-threaded) uygulamalarda nasıl güvenli hale getirebileceğinizi araştırmanızı öneririm.

Yasal Uyarı: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce hata yönetimi (exception handling) ve loglama mekanizmalarını eklediğinizden emin olun.

Bu yazıya tepkinizi paylaşın:
Mert Demir

Teknik konuları sadeleştirerek geniş kitlelere ulaştırma konusunda tutkuluyum. Ev işleri, bakım ve organizasyon temalı rehberlerimde kaliteyi ön planda tutuyorum.

Yorumlar (0)

Yorum Yaz