Gereksinimler ve Ön Hazırlık
Bu rehberi takip edebilmek için bilgisayarınızda Java Development Kit (JDK) 17 veya daha güncel bir sürümün yüklü olması önerilir. 2026 yılı standartları gereği, modern Java özelliklerini (record yapısı, lambda ifadeleri) kullanarak daha az kodla daha fazla işlevsellik elde edeceğiz.
- JDK 17+ (Tercihen JDK 21 LTS)
- IDE: IntelliJ IDEA, Eclipse veya VS Code
- Temel düzeyde Java Nesne Yönelimli Programlama (OOP) bilgisi
- Maven veya Gradle yapılandırma aracı (Opsiyonel)
Observer Tasarım Kalıbının Temel Bileşenleri
Observer kalıbı, iki temel aktör üzerine kuruludur: Subject (Özne) ve Observer (Gözlemci). Özne, durum değişikliklerini yöneten ana nesnedir; Gözlemci ise bu değişiklikleri dinleyen ve tepki veren taraftır.
Aşağıdaki tablo, bu yapının temel özelliklerini ve kullanım amaçlarını özetlemektedir:
| Bileşen | Görev | Avantaj |
|---|---|---|
| Subject | Gözlemcileri tutar ve bilgilendirir. | Gevşek bağlılık sağlar. |
| Observer | Güncellemeyi alır ve işler. | Modüler yapı sunar. |
| Interface | Sözleşme kurar. | Tip güvenliği sağlar. |
Adım 1: Observer Arayüzünü Oluşturma
İlk adımda, tüm gözlemcilerin uyması gereken sözleşmeyi tanımlıyoruz. Bu arayüz, özne tarafından tetiklenecek olan update metodunu barındırır.
public interface Observer {
void update(String message);
}
Bu arayüz, özne tarafından gönderilen veriyi işleyecek olan tüm sınıfların aynı metot imzasına sahip olmasını garanti eder. Böylece polimorfizm (çok biçimlilik) kullanarak farklı tipteki gözlemcileri aynı listede tutabiliriz.
Adım 2: Subject (Özne) Sınıfını Tasarlama
Özne sınıfı, gözlemcileri listesinde tutan ve olay gerçekleştiğinde onları bilgilendiren sınıftır. Modern Java'da List veya Set kullanarak gözlemcileri yönetebiliriz.
import java.util.ArrayList;
import java.util.List;
public class EventPublisher {
private final 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);
}
}
}
Bu kodda subscribe ve unsubscribe metotları ile gözlemcileri yönetiyoruz. notifyObservers metodu ise döngü içerisinde tüm kayıtlı gözlemcilerin update metodunu çağırır.
Adım 3: Gözlemci Sınıflarını Uygulama
Şimdi Observer arayüzünü uygulayan somut sınıflarımızı oluşturalım. Bu sınıflar, olay gerçekleştiğinde kendi iç mantıklarını çalıştıracaklardır.
public class EmailNotification implements Observer {
@Override
public void update(String message) {
System.out.println("E-posta gönderiliyor: " + message);
}
}
public class SmsNotification implements Observer {
@Override
public void update(String message) {
System.out.println("SMS gönderiliyor: " + message);
}
}
Bu sınıflar, sistemdeki farklı bildirim kanallarını temsil eder. İleride yeni bir bildirim kanalı (örneğin Push Notification) eklemek isterseniz, mevcut kodunuzu değiştirmeden sadece yeni bir sınıf oluşturmanız yeterli olacaktır.
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. Ana metodumuzda özneyi oluşturup gözlemcileri kaydedeceğiz.
public class Main {
public static void main(String[] args) {
EventPublisher publisher = new EventPublisher();
Observer email = new EmailNotification();
Observer sms = new SmsNotification();
publisher.subscribe(email);
publisher.subscribe(sms);
publisher.notifyObservers("Sistemde yeni bir kullanıcı kaydı gerçekleşti!");
}
}
Bu kod bloğu çalıştırıldığında, notifyObservers metodu hem e-posta hem de SMS nesnelerini tetikleyerek her ikisinin de kendi update metodunu çalıştırmasını sağlar.
Modern Yaklaşım: Lambda İfadeleri ile Kullanım
Java 8 ve sonrası ile gelen fonksiyonel programlama özellikleri sayesinde, Observer arayüzünü uygulamak için ayrı sınıflar oluşturmanıza gerek kalmayabilir. Lambda ifadeleri ile çok daha kısa kod yazabilirsiniz.
public class MainLambda {
public static void main(String[] args) {
EventPublisher publisher = new EventPublisher();
publisher.subscribe(msg -> System.out.println("Loglandı: " + msg));
publisher.subscribe(msg -> System.out.println("Veritabanına kaydedildi: " + msg));
publisher.notifyObservers("İşlem başarıyla tamamlandı.");
}
}
Bu yöntem, özellikle basit işlemlerde sınıf sayısını azaltarak projenin okunabilirliğini artırır. Ancak karmaşık iş mantığı içeren durumlarda ayrı sınıflar kullanmak hala en iyi pratiktir.
Kritik Güvenlik Uyarısı: Gözlemci kalıbını kullanırken, gözlemcilerin
updatemetotları içerisinde uzun süren işlemler yapmamasına dikkat edin. Eğer bir gözlemci hata verirse veya kilitlenirse, tüm bildirim zinciri durabilir. Bu durumu önlemek içintry-catchbloklarını kullanın ve mümkünse asenkron (ayrı thread) işlem yapmayı değerlendirin.
Sıkça Sorulan Sorular
Observer deseni ile PropertyChangeListener arasındaki fark nedir?
PropertyChangeListener, Java'nın eski sürümlerinden gelen yerleşik bir gözlemci mekanizmasıdır. Observer deseni ise daha genel bir mimari kalıptır ve kendi ihtiyaçlarınıza göre özelleştirilebilir.
Çok fazla gözlemci eklemek performansı etkiler mi?
Evet, çok sayıda gözlemci varsa notifyObservers metodu yavaşlayabilir. Bu durumda olayları kuyruğa alan (Event Queue) bir yapı kurmak daha verimlidir.
Gözlemcileri bellekten nasıl temizlerim?
Gözlemcileri unsubscribe metodu ile listeden kaldırmazsanız, "Memory Leak" (bellek sızıntısı) oluşabilir. Özellikle kısa ömürlü nesneleri gözlemci olarak eklerken dikkatli olun.
Observer kalıbı thread-safe midir?
Varsayılan haliyle değildir. Eğer çoklu thread (iş parçacığı) ortamında çalışıyorsanız, gözlemci listesini CopyOnWriteArrayList ile değiştirerek thread-safe hale getirebilirsiniz.
Modern Java'da Observer yerine ne kullanılabilir?
Java'nın java.util.Observable sınıfı artık önerilmemektedir (deprecated). Bunun yerine java.beans.PropertyChangeSupport veya RxJava/Project Reactor gibi kütüphaneler tercih edilmektedir.
Observer Tasarım Kalıbı İçin İleri Seviye Hata Yönetimi ve Debugging Stratejileri
Observer desenini karmaşık sistemlerde kullanırken, gözlemcilerden birinin hata fırlatması durumunda tüm bildirim zincirinin kesilmesi ciddi bir risk oluşturur. Bu durum, sistemin kararsız hale gelmesine veya kritik olayların kaçırılmasına neden olabilir. Sağlam bir mimari için bildirim döngüsünü korumalıyız.
Güvenli Bildirim Döngüsü Uygulaması
Aşağıdaki örnek, bir gözlemcinin hata alması durumunda diğerlerinin etkilenmemesi için try-catch bloğu ile izole edilmiş bir bildirim mekanizmasını göstermektedir:
public void notifyObservers(String eventData) {
for (Observer observer : observers) {
try {
observer.update(eventData);
} catch (Exception e) {
System.err.println("Gözlemci bildirim hatası: " + e.getMessage());
// Burada loglama mekanizması veya hata takip sistemi tetiklenebilir
}
}
}
Bu yaklaşım, özellikle üçüncü taraf kütüphanelerin veya dış servislerin gözlemci olarak eklendiği durumlarda sistemin "fail-safe" (hata anında güvenli) kalmasını sağlar.
Observer Deseninde Performans Optimizasyonu ve Bellek Yönetimi
Observer deseninde en sık karşılaşılan sorunlardan biri "Memory Leak" (bellek sızıntısı) durumudur. Eğer bir gözlemci, özne (subject) nesnesinden manuel olarak çıkarılmazsa, çöp toplayıcı (Garbage Collector) bu nesneyi bellekten temizleyemez. Bu durum, uzun süre çalışan uygulamalarda ciddi performans kayıplarına yol açar.
WeakReference Kullanımı
Bellek sızıntılarını önlemek için WeakReference sınıfını kullanmak, gözlemcilerin bellekten gerektiğinde silinebilmesine olanak tanır. İşte bu yaklaşımın temel bir uygulaması:
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class WeakSubject {
private List observers = new ArrayList();
public void addObserver(Observer observer) {
observers.add(new WeakReference(observer));
}
public void notifyObservers(String message) {
observers.removeIf(ref -> ref.get() == null); // Ölü referansları temizle
for (WeakReference ref : observers) {
Observer obs = ref.get();
if (obs != null) {
obs.update(message);
}
}
}
}
Performans İçin İpuçları
- Asenkron Bildirim: Eğer gözlemcilerin yapacağı işlemler uzun süreliyse,
notifyObserversmetodunu birExecutorServicekullanarak asenkron hale getirin. - Kopyalama ile Yineleme: Bildirim sırasında listeye yeni bir gözlemci eklenmesi veya çıkarılması
ConcurrentModificationExceptionhatasına yol açabilir. Bu yüzden yineleme yaparken listenin bir kopyasını (new ArrayList(observers)) kullanmak güvenlidir. - Filtreleme Mekanizması: Her gözlemciye her olayı göndermek yerine, gözlemcilerin belirli olay türlerine abone olabileceği bir "Event Type" filtreleme katmanı ekleyerek gereksiz işlem yükünü azaltın.
Bu stratejiler, Observer desenini sadece bir tasarım kalıbı olmaktan çıkarıp, yüksek trafikli ve ölçeklenebilir Java uygulamalarında güvenle kullanılabilir bir mimari bileşene dönüştürür.
Sonuç
Java ile Observer tasarım kalıbını uygulamak, sistem bileşenlerinizi birbirinden bağımsız hale getirerek kodunuzun esnekliğini artırır. Bu rehberde, temel bir Observer yapısının nasıl kurulacağını, modern Java özellikleri ile nasıl kısaltılacağını ve dikkat edilmesi gereken noktaları ele aldık. Bir sonraki adım olarak, bu yapıyı asenkron bir mesaj kuyruğu sistemiyle entegre etmeyi veya Java'nın Flow API yapısını inceleyerek reaktif programlamaya geçiş yapmayı deneyebilirsiniz.
Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanırken, hata yönetimi (exception handling), thread güvenliği ve bellek yönetimi (memory management) konularında gerekli ek güvenlik önlemlerini almanız sorumluluğunuzdadır.
Yorumlar (0)
Yorum Yaz