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

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

Gereksinimler ve Ön Hazırlık

Bu rehberdeki uygulamaları gerçekleştirmek için sisteminizde Java Development Kit (JDK) 17 veya üzeri bir sürümün yüklü olması önerilir. 2026 yılı standartlarında, Java'nın sunduğu modern arayüz (interface) ve lambda ifadeleri ile bu yapıyı oldukça kısa ve okunabilir bir şekilde kuracağız.

  • JDK 17+ (LTS sürüm olması kararlılık için önemlidir).
  • IDE: IntelliJ IDEA veya Eclipse (Modern Java desteği olan herhangi bir IDE).
  • Maven veya Gradle (Bağımlılık yönetimi için önerilir ancak bu örnekte saf Java kullanılacaktır).

Observer Arayüzünün Tanımlanması

İlk adım olarak, gözlemci nesnelerimizin ortak bir kontrata sahip olmasını sağlayacak arayüzü oluşturmalıyız. Bu arayüz, özne tarafından tetiklendiğinde çalışacak olan metodu tanımlar.

public interface Observer {
    void update(String message);
}

Burada tanımladığımız update metodu, özne (Subject) tarafından bir olay gerçekleştiğinde çağrılacaktır. Bu arayüz sayesinde, farklı türdeki gözlemcileri (örneğin; E-posta servisi, Log servisi) aynı yapı üzerinden yönetebiliriz.

Subject (Özne) Sınıfının Oluşturulması

Özne sınıfı, gözlemcileri listesinde tutan ve olay gerçekleştiğinde bu listeyi dolaşarak bildirim yapan sınıftır. Bu sınıfın gözlemcileri ekleme, çıkarma ve bilgilendirme yetenekleri olmalıdır.

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

public class EventManager {
    private List observers = new ArrayList();

    public void subscribe(Observer observer) {
        observers.add(observer);
    }

    public void unsubscribe(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

EventManager sınıfı, List yapısını kullanarak gözlemcileri bellekte tutar. notifyObservers metodu, bir döngü aracılığıyla kayıtlı tüm gözlemcilerin update metodunu tetikler.

Somut Gözlemci Sınıflarının Uygulanması

Şimdi Observer arayüzünü implemente eden somut sınıflarımızı oluşturalım. Bu sınıflar, bildirim geldiğinde kendi iş mantıklarını yürüteceklerdir.

public class EmailNotification implements Observer {
    @Override
    public void update(String message) {
        System.out.println("E-posta gönderiliyor: " + message);
    }
}

public class LoggingService implements Observer {
    @Override
    public void update(String message) {
        System.out.println("Log dosyasına yazılıyor: " + message);
    }
}

Bu yapıda, EmailNotification ve LoggingService sınıfları birbirinden bağımsızdır. Her ikisi de aynı olaydan haberdar olur ancak kendi görevlerine odaklanırlar.

Olay Yönetimini Test Etme

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

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

        Observer email = new EmailNotification();
        Observer logger = new LoggingService();

        manager.subscribe(email);
        manager.subscribe(logger);

        manager.notifyObservers("Sistem başlatıldı!");
    }
}

Kod çalıştırıldığında, hem e-posta hem de loglama servisinin aynı anda "Sistem başlatıldı!" mesajını işlediğini göreceksiniz. Bu, Observer deseninin gücünü gösterir.

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

Özellik Observer Deseni Doğrudan Çağrı
Bağımlılık Düşük (Loose Coupling) Yüksek (Tight Coupling)
Esneklik Yüksek (Yeni gözlemci kolay eklenir) Düşük (Kod değişikliği gerektirir)
Karmaşıklık Orta Düşük

Güvenlik Uyarısı: Observer desenini kullanırken, gözlemcilerin birbirini beklemediğinden emin olun. Eğer bir gözlemci çok yavaş çalışıyorsa, ana akışın (main thread) kilitlenmemesi için asenkron (eşzamansız) yapıları veya Java'nın CompletableFuture sınıfını kullanmayı değerlendirin.

Sıkça Sorulan Sorular

Observer deseni neden kullanılır?

Nesneler arasındaki bağımlılığı azaltmak ve sistemin genişletilebilirliğini artırmak için kullanılır. Bir nesne değiştiğinde, onu kullanan diğer nesneleri tek tek güncellemek yerine, bu desen sayesinde otomatik bildirim sağlanır.

Java'daki Observer sınıfı neden deprecated (kullanımdan kaldırıldı)?

Java'nın eski java.util.Observer ve java.util.Observable sınıfları, çoklu kalıtım kısıtlamaları ve thread-safety (iş parçacığı güvenliği) sorunları nedeniyle Java 9 itibarıyla deprecated edilmiştir. Bu yüzden kendi arayüzümüzü yazmak en güvenli yoldur.

Gözlemciler arasında veri paylaşımı nasıl yapılır?

Gözlemciler arasında doğrudan veri paylaşımı yerine, update metoduna bir DTO (Data Transfer Object) parametresi göndererek verinin tüm gözlemcilere tutarlı bir şekilde iletilmesini sağlayabilirsiniz.

Bu yapı çoklu iş parçacığında (multithreading) güvenli midir?

Yukarıdaki basit örnek thread-safe değildir. Eğer aynı anda birden fazla iş parçacığı gözlemci ekleyip çıkaracaksa, List yerine CopyOnWriteArrayList kullanmanız önerilir.

Observer deseni ile Event Listener arasındaki fark nedir?

Aslında Observer deseni, Event Listener yapısının temelini oluşturur. Event Listener, bu desenin daha spesifik ve genellikle UI (kullanıcı arayüzü) bileşenleri için özelleştirilmiş halidir.

Observer Deseninde Performans Optimizasyonu ve Bellek Yönetimi

Observer tasarım kalıbı, özellikle çok sayıda gözlemcinin (observer) sisteme dahil olduğu durumlarda bellek sızıntılarına (memory leak) yol açabilir. Bir gözlemci, özneye (subject) kayıtlı olduğu sürece, çöp toplayıcı (Garbage Collector) tarafından temizlenemez. Bu durumu engellemek için WeakReference yapısını kullanmak kritik bir öneme sahiptir.

Zayıf Referanslar ile Bellek Sızıntılarını Önleme

Gözlemcilerinizi doğrudan bir ArrayList içerisinde tutmak yerine, onları WeakReference ile sarmalayarak öznenin gözlemcileri "zayıf" bir şekilde tutmasını sağlayabilirsiniz. Böylece, gözlemci nesnesi başka bir yerde kullanılmadığında, özne onu otomatik olarak serbest bırakabilir.

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class WeakSubject {
    private List observers = new ArrayList();

    public void attach(Observer observer) {
        observers.add(new WeakReference(observer));
    }

    public void notifyObservers(String event) {
        for (WeakReference ref : observers) {
            Observer obs = ref.get();
            if (obs != null) {
                obs.update(event);
            }
        }
        // Temizlik işlemi: null olan referansları listeden kaldır
        observers.removeIf(ref -> ref.get() == null);
    }
}

Observer Deseninde Hata Ayıklama (Debugging) Stratejileri

Observer deseninde olay akışını takip etmek, kodun dağıtık yapısı nedeniyle bazen karmaşık olabilir. Hata ayıklama sürecini kolaylaştırmak için şu yöntemleri izleyebilirsiniz:

  • Olay Günlüğü (Event Logging): Her bildirim gönderildiğinde bir log mekanizması kullanarak hangi gözlemcinin hangi veriyi aldığını kayıt altına alın.
  • StackTrace Analizi: Bildirim zincirinin nerede koptuğunu anlamak için Thread.currentThread().getStackTrace() metodunu kullanarak tetikleyici kaynağı belirleyin.
  • Gözlemci İsimlendirme: Her gözlemciye benzersiz bir kimlik veya isim vererek, loglarda hangi nesnenin işlem yaptığını kolayca ayırt edin.
Profesyonel İpucu: Eğer uygulamanızda çok fazla olay tetikleniyorsa, gözlemcilerin update() metodunda ağır işlemler yapmaktan kaçının. Bu tür işlemleri ayrı bir ExecutorService üzerinde asenkron olarak çalıştırmak, ana uygulamanızın donmasını engeller.

Asenkron Bildirim Mekanizması

Bildirimlerin senkron yapılması, gözlemcilerden birinin hata alması durumunda tüm zinciri durdurabilir. Bunu aşmak için bildirimleri bir kuyruk yapısı üzerinden yönetebilirsiniz:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncSubject {
    private final ExecutorService executor = Executors.newFixedThreadPool(4);

    public void notifyObservers(String event) {
        for (Observer obs : observers) {
            executor.submit(() -> obs.update(event));
        }
    }
}

Sonuç

Java ile Observer tasarım kalıbı kullanarak olay yönetimi yapmak, özellikle kurumsal çaplı projelerde kodunuzun bakımını kolaylaştırır. Bu makalede adım adım öğrendiğiniz yapı, nesne yönelimli programlamanın temel taşlarından biridir. Bir sonraki aşamada, bu yapıyı java.util.concurrent paketindeki araçlarla birleştirerek asenkron olay yönetimi üzerine çalışmanızı öneririm. Kodunuzun temiz ve sürdürülebilir kalması için bu tür tasarım kalıplarını projelerinizde uygulamaktan çekinmeyin.

Bu yazıya tepkinizi paylaşın:
Zeynep Kaya

Ev yönetimi ve kendin yap (DIY) projeleri konusunda uzmanlaşmış bir içerik üreticisiyim. Detaylı rehberler hazırlayarak okuyucuların evdeki küçük sorunları profesyonel yardıma gerek duymadan çözmelerini sağlıyorum.

Yorumlar (0)

Yorum Yaz