Tag: Observer Design Pattern

Observer Design Pattern

Observer Design Pattern (Gözlemci Tasarım Deseni), iki ana bileşenden oluşur; Subject ve Observer…

Subject, üzerinde değişimlerin gözlendiği, gözlenmek istendiği bileşendir. Observer ise bir subject’teki değişimleri gözlemlemek için kullanılan bileşendir.

Observer Design Pattern’da, bir subject’teki değişimden, onu gözleyenler haberdar edilir.

Observer Pattern’ı bir başarımlar sistemi kurmak için kullanabiliriz. Buna yönelik bir senaryo belirleyip, bu senaryoyu Observer Design Pattern kullanarak çözelim.

Senaryo

Oyunumuzda bir skor verisi olduğunu düşünelim. Bu skor verisindeki değişiklikleri gözleyen 3 adet de başarımımız olsun. Başarımlarımız kendi hedef skorlarına geldiklerinde açılsınlar ve başarılmış olsunlar.

Adım 1

ScoreDataSubject isimli bir nesne oluşturalım. Bu nesne, dinleyeceğimiz nesne. Bütün observer’larımız, bu nesneye kayıt olup, bu nesnedeki değişimleri dinleyecekler.

ScoreDataSubject

public class ScoreDataSubject {

private int score;
private List<ScoreDataObserver> _observers = new List<ScoreDataObserver>();

public void Attach(ScoreDataObserver observer)
{
_observers.Add(observer);
}

public void Detach(ScoreDataObserver observer)
{
_observers.Remove(observer);
}

public void Notify()
{
for (int i = 0; i < _observers.Count; i++)
{
_observers[i].OnNotify();
}
}

public int GetScore()
{
return score;
}

public void SetScore(int score)
{
this.score = score;
Notify();
}
}

ScoreDataSubject nesnemiz, score datamızı ve kendini dinleyen observer’ların listesini tutuyor. Bu subject’teki değişimleri dinlemek isteyen observer’lar Attach() metodunu kullanarak kendilerini bu nesneye kaydediyorlar. Detach() metodunu kullanan observerlar da kendilerini gözlemciler listesinden silmiş oluyorlar. Notify() metoduna gelecek olursak, bu metod da subject’teki değişimlerin observer listesindeki her bir elemana gönderilmesini sağlıyor.

Kurduğumuz sistemde, SetScore() metodu çağrıldığında, yani her yeni skor set edildiğinde, ScoreDataSubject’e kaydolmuş bütün observer’lar bu skordan haberdar olacaklar.

Adım 2

Bu adımda da ScoreDataObserver’ımızı yazalım. Pattern’ımızdaki diğer tüm observer’lar bu sınıftan türetilecek.

ScoreDataObserver

public class ScoreDataObserver
{
protected ScoreDataSubject subject;

public ScoreDataObserver(ScoreDataSubject subject)
{
this.subject = subject;
subject.Attach(this);
}

public virtual void OnNotify()
{

}

}

Her observer constructor’ında kendini dinleyeceği subject’e kaydediyor. Böylece ondaki bütün değişimleri takip etmiş oluyor.

Adım 3

Bu adımda da Achievement(Başarım) nesnemizi oluşturalım ve ardından belirleyeceğimiz 3 başarımı Achievement base sınıfından türetelim. Bu üç başarımımız şunlar olacaklar; BabySteps, SuperScorer ve ImmortalPlayer…

Achievement

public class Achievement : ScoreDataObserver
{
protected float target;

public Achievement(ScoreDataSubject subject) : base(subject)
{
}

public override void OnNotify()
{
if (subject.GetScore() > target)
{
Achieve();
subject.Detach(this);
}
}

protected virtual void Achieve()
{

}
}

Dikkatinizi OnNotify() metoduna çekmek istiyorum. OnNotify() metodu her çağrıldığında biliyoruz ki subject’te bir değişiklik var. Biz burada subject’teki skor değişimini dinliyoruz ve başarımımızın hedefinden büyük mü, kontrol ediyoruz. Eğer skor, hedef değerimizden büyükse, Achieve() metodunu çağırıyoruz ve başarımımızı gerçekliyoruz. Ardından artık skor datasını dinlemeye ihtiyacımız olmadığı için, Achievement gözlemci nesnemizi gözlemlediğimiz subject’ten Detach() metodunu kullanarak koparıyoruz.

BabySteps

public class BabySteps : Achievement {

public BabySteps(ScoreDataSubject subject) : base(subject)
{
target = 1;
}

protected override void Achieve()
{
Debug.Log("Baby Steps Achieved!!!");
}
}

SuperScorer

public class SuperScorer : Achievement
{
public SuperScorer(ScoreDataSubject subject) : base(subject)
{
target = 19;
}

protected override void Achieve()
{
Debug.Log("Super Scorer Achieved!!!");
}
}

ImmortalPlayer

public class ImmortalPlayer : Achievement
{
public ImmortalPlayer(ScoreDataSubject subject) : base(subject)
{
target = 60;
}

protected override void Achieve()
{
Debug.Log("Immortal Player Achieved!!!");
}
}

Adım 4

Demomuzu yazalım ve senaryomuzu gerçeklemiş olalım.

using UnityEngine;
public class ObserverDesignPatternDemo : MonoBehaviour {

private ScoreDataSubject subject = new ScoreDataSubject();

void Start ()
{
var superScorer = new SuperScorer(subject);
var babySteps = new BabySteps(subject);
var immortalPlayer = new ImmortalPlayer(subject);
}

StartCoroutine("IncreaseScore");
}

Demomuzun Start metodu içinde başarımlarımızı yarattık ve ardından IncreaseScore Coroutine’ini başlattık. IncreaseScore() coroutine’i skoru her yarım saniyede bir artırıyor ve skordaki değişim bütün observerlara bildiriliyor. Başarılan başarımlar kendilerini kaydoldukları subject’ten koparıyor ve en sonunda skoru dinleyen hiç başarım kalmıyor. Ve böylece senaryomuzu gerçeklemiş oluyoruz.

Geliştiriciye şuradan bir kahve ısmarlanabiliyormuş. 🙂

Saygılar.