GoF’a göre, Observer tasarım deseninin amacı;
“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.”
“Nesneler arasında birden çoğa bağımlılık tanımlayın, böylece bir nesne durum değiştirdiğinde, tüm bağımlıları otomatik olarak bilgilendirilir ve güncellenir.”
Bu pattern nesnenin durumunu/davranışlarını inceler ve ona göre operasyon/lar geliştirir. Gerçek hayattan örnek verecek olursak, bir borsa app yapısını düşünebilirsiniz x bir firmanın hissesisin düşüşüne veya yükselişine göre kullanıcılarınızı otomatik olarak uyaracak ve kullanıcılarınız gelişen duruma göre alım/satım yapacaktır. Burada tabiki de bir yatırımcının birden fazla hissesi ve farklı kullanıcılarda olacaktır. Böyle bir senaryoda app x hissenin durumunu izlemekte olan tüm kullanıcılara duruma göre otomatik olarak bilgilendirecektir. İşte bu senaryo Observer Design Pattern’ı kapsamaktadır.
MVC (Model View Controller) Observer patern’in en sık kullanıldığı mimaridir. MVC’de Model ile modeli izleyen View nesneleri Modeldeki her bir değişiklikten haberdar olmak için Observer modelini kullanır.
UML Class Diyagramı
Kullanım Sıklığı: yüksek
Observer Prensipleri
Pattern’e ait temel unsurlar; reaktif bir nesne (Concreate Subject), durumundaki herhangi bir değişikliği bildirmek için bir gözlemci listesi içerir, bu nedenle, gözlemcilerin kendilerinin kaydolabileceği ve kaydını silebileceği metotları sağlamalıdır. Concreate Subject ayrıca herhangi bir değişikliği tüm gözlemcilere bildirmek için bir metot içerir ve gözlemciye bildirirken güncellemeyi gönderebilir veya güncellemeyi almak için başka bir metot sağlayabilir. Gözlemcinin, izlenecek nesneyi ayarlamak için bir metodu ve Concreate Subject tarafından güncellemeleri bildirmek için kullanılacak başka bir metot olmalıdır.
C# Observer Design Pattern Uygulama
Bu senaryomuzda konuların ve takipçilerin olduğu basit bir konu uygulaması planlayacağız. Takipçiler bir konuya kayıt olabilecek. Konuya yeni bir mesaj gönderildiğinde, tüm takipçiler bilgilendirilecek.
Konunun gerekliliklerine göre, herhangi bir somut konu tarafından uygulanacak sözleşme metotlarını tanımlayan temel Konu Interface’i
public interface ISubject
{
//Takipçileri kaydetme ve kayıtlarını silme metotları
public void register(IObserver obj);
public void unregister(IObserver obj);
//Değişikliği takipçiye bildirmek için metot
public void notifyObservers();
//konudan güncellemeleri alma metodu
public Object getUpdate(IObserver obj);
}
Takipçiler için bir sözleşme oluşturacağız, Konuyu takipçiye bağlamak için bir metot ve herhangi bir değişikliği bildirmek için Konu tarafından kullanılacak başka bir metot olacaktır.
public interface IObserver
{
//Konu tarafından kullanılan takipçinin güncelleme metodu
public void update();
//Takipçiyi konu ile bağlamak için.
public void setSubject(ISubject sub);
}
Artık sözleşmemiz hazır, konumuzun somut uygulamasına geçelim.
public class Subject : ISubject
{
private List<IObserver> observers;
private String message;
private bool changed;
private Object MUTEX= new Object();
public Subject()
{
observers = new List<IObserver>();
}
public object getUpdate(IObserver obj)
{
return this.message;
}
public void notifyObservers()
{
List<IObserver> observerslocal = null;
//lock, mesaj alındıktan sonra kaydedilen herhangi bir gözlemcinin bilgilendirilmediğinden emin olmak için kullanılır.
lock (MUTEX)
{
if (!changed)
{
return;
}
observerslocal = new List<IObserver>(this.observers);
this.changed = false;
}
foreach (IObserver obj in observerslocal)
{
obj.update();
}
}
public void register(IObserver obj)
{
if (obj == null)
{
throw new Exception("Null Observer");
}
else
{
lock (MUTEX)
{
if (!observers.Contains(obj))
{
observers.Add(obj);
}
}
}
}
public void unregister(IObserver obj)
{
lock (MUTEX)
{
observers.Remove(obj);
}
}
//Konuya mesaj gönderme metodu
public void postMessage(String msg)
{
Console.WriteLine("Message Posted to Topic:" + msg);
this.message = msg;
this.changed = true;
notifyObservers();
}
}
Bir takipçiyi kaydetmek ve kaydını silmek için register ve unregister metotları yazdık, ekstra olarak, istemci uygulaması tarafından konuya String mesajı göndermek için kullanılacak olan postMessage() metodu yazdık. Konunun durumundaki değişikliği takip etmek ve takipçileri bilgilendirmek için kullanılan boolean değişkenine dikkat edin. Bu değişken, güncelleme olmadığında ve birisi notifyObservers() metodunu çağırdığında, takipçilere yanlış bildirimler göndermemesi için gereklidir. Ayrıca, bildirimin yalnızca mesaj konuya yayınlanmadan önce kayıtlı gözlemcilere gönderilmesini sağlamak için notifyObservers() metodunda senkronizasyon kullanımına dikkat edin.
Konuyu takip edecek Takipçilerin uygulaması.
public class SubjectSubscriber : IObserver
{
private string name;
private ISubject topic;
public SubjectSubscriber(string nm)
{
this.name = nm;
}
public void setSubject(ISubject sub)
{
this.topic = sub;
}
public void update()
{
string msg = (string)topic.getUpdate(this);
if (msg == null)
{
Console.WriteLine(name + ":: No new message");
}
else
{
Console.WriteLine(name + ":: Consuming message::" + msg);
}
}
}
İletinin tüketilmesini sağlamak için Konu getUpdate() metodonu çağırdığı yerde update() metodunun uygulanmasına dikkat edin. Bu çağrıyı, update() metoduna argüman olarak mesaj ileterek önleyebilirdik.
İşte konu uygulamamızı tüketmek için basit bir test programı.
Program.cs – Main Method
//Konu oluştur
Subject topic = new Subject();
//Takipçi oluştur.
IObserver obj1 = new SubjectSubscriber("Obj1");
IObserver obj2 = new SubjectSubscriber("Obj2");
IObserver obj3 = new SubjectSubscriber("Obj3");
//Takipçileri konuya kaydeedin.
topic.register(obj1);
topic.register(obj2);
topic.register(obj3);
//Takipçileri konuya bağla
obj1.setSubject(topic);
obj2.setSubject(topic);
obj3.setSubject(topic);
//Herhangi bir güncelleme olup olmadığını kontrol edin
obj1.update();
//Konuya bir mesaj gönderin.
topic.postMessage("New Message");
Sonuç
Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message