Category: Design Pattern

Factory Design Pattern

Manager Class : Merhaba, ben bir A nesnesi yaratmak istiyorum.

Geliştirici : O zaman Factory nesnesine başvurman gerek.

Factory design pattern’ı yukarıdaki kısa konuşma özetliyor aslında. Nesne yaratmak için Factory nesneleri oluştururuz ve nesne yaratmak istediğimizde, Factory nesnelerine başvururuz. Gelin Unity’de basit bir örneği gerçekleyelim.

Uygulayacağımız Örneğin Senaryosu

Bir shape renderer’ımız olacak. Bu renderer içinde bir shape listesi bulunduracak. Bu örneğe özel olarak, renderer, ShapeFactory nesnemize başvurarak render etmek istediği nesneleri alacak. Ve render edecek.

Adım 1

Bu adımda ana sınıfımızı, yani Shape sınıfını yazacağız.

using UnityEngine;
public class Shape 
{
    protected Vector3 _center;
    
    public virtual void Draw()
    {
         
    }
}

Shape sınıfımız, bütün diğer Shape’lerde olacak olan, _center Vector3’ünü taşıyor. Ve gene diğer tüm Shape nesnelerinde olması gereken Draw() metodunu bünyesinde barındırıyor.

Adım 2

Bu adımda Cube ve Sphere nesnelerimizi oluşturalım ve onları Shape nesnesinden türetelim.

Cube Nesnesi

using UnityEngine;
public class Cube : Shape
{
    private Vector3 _size;
    
    public Cube(Vector3 center, Vector3 size)
    {
        _center = center;
        _size = size;
    }
    
    public override void Draw()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawCube(_center, _size);
    }
}

Cube nesnesi, türetildiği Shape nesnesine ek olarak, bir de _size değişkeni barındırıyor. _size(yükseklik, genişlik, derinlik) olarak anlaşılmalıdır.

Sphere Nesnesi

using UnityEngine;
public class Sphere : Shape
{
    private float _radious;
    
    public Sphere(Vector3 center, float radius)
    {
        _center = center;
        _radious = radius;
    }
    public override void Draw()
    {
        Gizmos.color = Color.black;
        Gizmos.DrawSphere(_center, _radious);
    }
}

Sphere nesnesi türetildiği Shape nesnesinin değişkenlerine ek olarak bir de radious değişkeni tutuyor. _radious değişkeni, çizeceğimiz Sphere’in yarı çapıdır.

Cube ve Sphere nesnelerinin Constructor’larında, nesnelere center ve ilgili değişkeni atanmakta, nesneler oluşturulmaktadır. Ayrıca bu nesneler Draw metodunu override ederek, bu metodu kendilerine göre özelleştirmektedirler.

Adım 3

Bu adımda ShapeFactory nesnemizi oluşturacağız. Ve bir de Shape tiplerimizi tanımlayacağız.

public enum ShapeType
{
    cube,
    sphere
}

Cube ve Sphere olmak üzere iki adet shape tipi tanımladık.

using UnityEngine;
public class ShapeFactory 
{
    public Shape GetShape(ShapeType shapeType)
    {
        switch (shapeType)
        {
            case ShapeType.cube:
                return new Cube(new Vector3(10,10,10), new Vector3(50,70,90));
            case ShapeType.sphere:
                return new Sphere(new Vector3(80,10,10), 30f);
            default:
                return null;
        }
    }
}

Artık nesne yaratılmak istendiğinde başvuracağımız bir Factory nesnemiz var.

Adım 4

using System.Collections.Generic;
using UnityEngine;
public class ShapeRenderer : MonoBehaviour {
    private List<Shape> Shapes;
    void Start() 
    {
        CreateShapes();
    }
    private void CreateShapes()
    {
        ShapeFactory shapeFactory = new ShapeFactory();
        Shapes = new List<Shape>
        {
            shapeFactory.GetShape(ShapeType.cube),
            shapeFactory.GetShape(ShapeType.sphere)
        };
    }
    private void OnDrawGizmos()
    {
        RenderShapes();
    }
    private void RenderShapes()
    {
        if (Shapes != null)
        {
            for (int i = 0; i < Shapes.Count; i++)
            {
                Shapes[i].Draw();
            }
        }
    }
}

Shape Renderer nesnesi, Start() metodu çağrıldığında Draw() metodunu çağıracağı nesneleri, ShapeFactory’den alır ve OnDrawGizmos() metodu içinde bu nesnelerin Draw() metodlarını çağırarır.

Sonuç olarak, Scene ekranında kırmızı bir cube ve siyah bir sphere nesnesi görürürüz. Sadece Scene ekranında görmemizin sebebi, Gizmos.DrawSphere ve Gizmos.DrawCube metodlarının Scene sahnesinde görülebilecek test çizimleri yapmasıdır. Bu eğitim yazısının asıl amacı Factory design pattern’ı anlatmak olduğu için, Draw.Gizmos metodları kullanılmıştır.

Sorularınız ve önerileriniz için lütfen yorum yapmaktan çekinmeyin. Bana, cosgun.halil@gmail.com adresinden ulaşabilirsiniz.

Saygılar.

Singleton Design Pattern

Singleton’ı şu iki maddede özetleyebiliriz.

  1. Bir sınıfın bir örneği olacağını garanti eder.
  2. Bu sınıfı global bir erişim noktası yapar.

Bir design pattern’ı kullanmadan önce onun zayıf yanlarını iyi bilmek gerek. Profesyonel hayatta, her yanından Singleton fışkıran kodlara gülündüğüne bir çok kez şahit oldum. Bu kahkahaların sebebini araştırırken okuduğum ve içlerinden notlar aldığım bir kaç yazı var. Singleton’ı uygulamadan önce bu notları sizlerle paylaşmak isterim.

Use Your Singletons Wisely (Singletonlarınızı Bilgece Kullanın)

Yazı, neyin gerçekten Singleton olması gerektiğini sorguluyor. Bu sorgulamayı yapmadan önce, Singleton’ın sınıflar arası bağımlığı artırdığını belirtiyor. Yani Singleton’ın göründüğü kadar masum olmadığı konusunda yazar ile aynı fikirdeyiz.

Yazının can alıcı yerine geldiğimizde Singleton uygulanmadan sorulması gereken 3 soru ile karşılaşıyoruz. Bana sorarsanız, bunlar bir hayli haklı sorular.

  1. Bu sınıfı bütün uygulamalar aynı şekilde mi kullanacak? “Aynı şekilde” kalıbı burada belirleyici unsur. — (Yanıt: Bu sınıf eğer bir Singleton’sa bütün uygulamalar tarafından aynı şekilde kullanılmalılar. )
  2. Bu sınıfı kullanacak her uygulamanın bu sınıfın her zaman ve sadece bir örneğine mi ihtiyacı olacak? (Yanıt: Bu sınıf eğer bir Singleton’sa bu sınıftan sadece bir adet instance yani örnek olmalı. Bu sınıfı kullanacak her uygulama, her zaman bu sınıfın sadece bir örneğine ihtiyaç duyacak.)
  3. Bu sınıfın istemcileri bir parçası oldukları uygulamadan bihaber olmalılar mı? (Yanıt : Evet, bu sınıfın istemcileri parçası oldukları uygulamadan bihaber olmalılar.)

Bu üç soruya yanıtımız evetse, bu sınıfı Singleton yapmakla doğru yapıyoruz.

IBM’in bu yazısında örnek olarak bir Logger verilmiş. Logger belki de verilebilecek en güzel örnek. Bütün uygulamalarda, uygulamanın bütün parçalarında log bastırılmak istenebilir. Logger nesnesinin Singleton olmasında kesinlikle bir engel yok.

Why Singletons Are Evil

Burada da Singleton’ların şeytan olduklarına dair 4 kanıt sunulmuş. Aldığım notları sizlerle paylaşıyorum.

  1. Singleton’lar kodun her yerinden erişilebilirlerse, bu onları global değişkenlerin içinde bulunduğu duruma sokar. Global değişkenler kötüdür değil mi?
  2. Singleton’lar nesne yaratmayı sınırlandırmanıza izin verir. Bu da SOLID’in ilk kuralı Single Responsibility’e ters düşer. Oysa ki, Singleton bir sınıf, Singleton olup olmadığı ile ilgilenmemelidir. (Single Responsibility der ki, bir sınıfın yalnızca bir görevi olmalıdır. Yani Logger’ınız sadece log basmalıdır, gidip belleğe veri yazmamalıdır. )
  3. Singleton’lar sınıflar arasındaki bağımlılığı artırır. Bu da kodun test edilebilirliğini azaltır. Bu durumdan kurtulmak için nesnelerin içine referans yollama yoluna başvurabilirsiniz.
  4. Singleton’lar program sonlanana kadar durumlarını korurlar. Singletonlar gibi durumunu koruyan kod parçaları Unit Test’in düşmanıdırlar.

Patterns I Hate #1 Singleton

Singleton’ın neden kullanılmaması gerektiğini anlatan elimdeki en iyi yazı bu. Bu yazıdan aldığım ilk notsa, her okuduğumda çarpıcı bir etki bırakıyor bende.

“Henüz yolun başındaki geliştiriciler, yardımcı sistemlerini bozarak onlara Singleton yoluyla ulaşırlar.”

Yazının başında bahsettiğim kahkahaların sebebini bu yazıda buluyorum. Singleton kullanarak bir sisteme ulaşmak çok kolay. Ana bir sistemin yardımcı sistemlerine doğru kod akışını tasarlayamadığımız anda aklımıza bir kaçış şekli olarak Singleton düşüyor. Ve dolayısıyla yardımcı sistemimizi bozuyor ve ona Singleton yardımıyla ulaşıyoruz.

“Kod bağımlılığını saklarlar”

Yazar bu sözü açıklarken Singleton’ı Kudzu’ya benzetiyor. Kudzu bir Japon sarmaşığı. Kendi tecrübelerimden yola çıkarak konuşacak olursam, bir hayli haklı. Başta masum ve şirin duran, fakat projenin sonlarına doğru kodumun her yanını sarmış Singleton’lar geliyor aklıma. Kod bağımlılığının Singleton tarafından böyle ustaca saklanması, bu sorunun gözümüzden kaçmasına sebep oluyor ve dolayısıyla çözüm aramıyoruz.

“Test etmeyi zorlaştırıyor”

Daha önceki notlarda da bu duruma parmak basılmıştı. Singleton kod içindeki bağımlılığı artırdığı için, test edilebilirliği düşürüyor.

“Alt sınıflamaya uygun değil”

Singleton bir sınıftan bir alt sınıf türetmek, Singleton’ın doğasına aykırı. Singleton’lar statiktir, yani onlardan sadece bir tane bulunur. Hatta bunu garanti etmek için, yazdığımız Singleton’lara private bir constructor yazarız. Ki onlardan herhangi bir sınıf miras alamasın.

Paylaşacağım notlar burada son buluyor. Singleton ile ilgili biraz daha okuma yapmak isterseniz, yazının sonunda kaynak önerilerinde bulunacağım.

Use Your Singletons Wisely (Singletonlarınızı Bilgece Kullanın)

Yazı, neyin gerçekten Singleton olması gerektiğini sorguluyor. Bu sorgulamayı yapmadan önce, Singleton’ın sınıflar arası bağımlığı artırdığını belirtiyor. Yani Singleton’ın göründüğü kadar masum olmadığı konusunda yazar ile aynı fikirdeyiz.

Yazının can alıcı yerine geldiğimizde Singleton uygulanmadan sorulması gereken 3 soru ile karşılaşıyoruz. Bana sorarsanız, bunlar bir hayli haklı sorular.

  1. Bu sınıfı bütün uygulamalar aynı şekilde mi kullanacak? “Aynı şekilde” kalıbı burada belirleyici unsur. — (Yanıt: Bu sınıf eğer bir Singleton’sa bütün uygulamalar tarafından aynı şekilde kullanılmalılar. )
  2. Bu sınıfı kullanacak her uygulamanın bu sınıfın her zaman ve sadece bir örneğine mi ihtiyacı olacak? (Yanıt: Bu sınıf eğer bir Singleton’sa bu sınıftan sadece bir adet instance yani örnek olmalı. Bu sınıfı kullanacak her uygulama, her zaman bu sınıfın sadece bir örneğine ihtiyaç duyacak.)
  3. Bu sınıfın istemcileri bir parçası oldukları uygulamadan bihaber olmalılar mı? (Yanıt : Evet, bu sınıfın istemcileri parçası oldukları uygulamadan bihaber olmalılar.)

Bu üç soruya yanıtımız evetse, bu sınıfı Singleton yapmakla doğru yapıyoruz.

IBM’in bu yazısında örnek olarak bir Logger verilmiş. Logger belki de verilebilecek en güzel örnek. Bütün uygulamalarda, uygulamanın bütün parçalarında log bastırılmak istenebilir. Logger nesnesinin Singleton olmasında kesinlikle bir engel yok.

Why Singletons Are Evil

Burada da Singleton’ların şeytan olduklarına dair 4 kanıt sunulmuş. Aldığım notları sizlerle paylaşıyorum.

  1. Singleton’lar kodun her yerinden erişilebilirlerse, bu onları global değişkenlerin içinde bulunduğu duruma sokar. Global değişkenler kötüdür değil mi?
  2. Singleton’lar nesne yaratmayı sınırlandırmanıza izin verir. Bu da SOLID’in ilk kuralı Single Responsibility’e ters düşer. Oysa ki, Singleton bir sınıf, Singleton olup olmadığı ile ilgilenmemelidir. (Single Responsibility der ki, bir sınıfın yalnızca bir görevi olmalıdır. Yani Logger’ınız sadece log basmalıdır, gidip belleğe veri yazmamalıdır. )
  3. Singleton’lar sınıflar arasındaki bağımlılığı artırır. Bu da kodun test edilebilirliğini azaltır. Bu durumdan kurtulmak için nesnelerin içine referans yollama yoluna başvurabilirsiniz.
  4. Singleton’lar program sonlanana kadar durumlarını korurlar. Singletonlar gibi durumunu koruyan kod parçaları Unit Test’in düşmanıdırlar.

Patterns I Hate #1 Singleton

Singleton’ın neden kullanılmaması gerektiğini anlatan elimdeki en iyi yazı bu. Bu yazıdan aldığım ilk notsa, her okuduğumda çarpıcı bir etki bırakıyor bende.

“Henüz yolun başındaki geliştiriciler, yardımcı sistemlerini bozarak onlara Singleton yoluyla ulaşırlar.”

Yazının başında bahsettiğim kahkahaların sebebini bu yazıda buluyorum. Singleton kullanarak bir sisteme ulaşmak çok kolay. Ana bir sistemin yardımcı sistemlerine doğru kod akışını tasarlayamadığımız anda aklımıza bir kaçış şekli olarak Singleton düşüyor. Ve dolayısıyla yardımcı sistemimizi bozuyor ve ona Singleton yardımıyla ulaşıyoruz.

“Kod bağımlılığını saklarlar”

Yazar bu sözü açıklarken Singleton’ı Kudzu’ya benzetiyor. Kudzu bir Japon sarmaşığı. Kendi tecrübelerimden yola çıkarak konuşacak olursam, bir hayli haklı. Başta masum ve şirin duran, fakat projenin sonlarına doğru kodumun her yanını sarmış Singleton’lar geliyor aklıma. Kod bağımlılığının Singleton tarafından böyle ustaca saklanması, bu sorunun gözümüzden kaçmasına sebep oluyor ve dolayısıyla çözüm aramıyoruz.

“Test etmeyi zorlaştırıyor”

Daha önceki notlarda da bu duruma parmak basılmıştı. Singleton kod içindeki bağımlılığı artırdığı için, test edilebilirliği düşürüyor.

“Alt sınıflamaya uygun değil”

Singleton bir sınıftan bir alt sınıf türetmek, Singleton’ın doğasına aykırı. Singleton’lar statiktir, yani onlardan sadece bir tane bulunur. Hatta bunu garanti etmek için, yazdığımız Singleton’lara private bir constructor yazarız. Ki onlardan herhangi bir sınıf miras alamasın.

Paylaşacağım notlar burada son buluyor. Singleton ile ilgili biraz daha okuma yapmak isterseniz, yazının sonunda kaynak önerilerinde bulunacağım.

public class LB_Logger
{
    private static readonly LB_Logger instance = new LB_Logger();

    static LB_Logger()
    {
    }

    private LB_Logger()
    {
    }

    public static LB_Logger Instance
    {
        get
        {
            return instance;
        }
    }

    public delegate void PrintLogDelegate(string log);
    public event PrintLogDelegate PrintLogEvent;
    private string logString = String.Empty;

    public void PrintLog(string log)
    {
        if (logString != string.Empty)
        {
            logString += Environment.NewLine;
        }
        logString += log;
        if (PrintLogEvent != null)
        {
            PrintLogEvent(logString);
        }
    }

    public string GetLogString()
    {
        return logString;
    }
}

Kodun akıllarda soru işareti oluşturabilecek yanlarını açıklayalım.

private static readonly LB_Logger instance = new LB_Logger();

LB_Logger’ın bir örneğini tutuyoruz ve bu örnekten sadece bir tane olacağını garanti ediyoruz.

static LB_Logger()
{
}

C# compiler’a beforefieldinit olarak işaretleme yapmamasını söylüyoruz. Böylece Singleton örneğimiz ilk ihtiyaç duyulduğu zaman yaratılıyor. Daha detaylı bilgi için bu yazıyı okuyunuz.

public static LB_Logger Instance
{
    get
      {
         return instance;
      }
}

Singleton nesnemizin yaratılan örneğine ulaşımı da bu Instance ile sağladık.

Kodun herhangi bir noktasından, LB_Logger.Instance.PrintLog(“Test”) örneğindeki gibi bir log yollayabiliriz. LB_Logger bu logu alır ve basılan diğer loglara ekler. Ve ardından PrintLogEvent eventini dinleyen bütün dinleyicilere bu log stringini dağıtır.

Adım 3 — LB_Monitor

using UnityEngine;
public class LB_LoggerMonitor : MonoBehaviour {
    public TMPro.TextMeshProUGUI LogTextContainer;
	void OnEnable ()
    {
        LB_Logger.Instance.PrintLogEvent += MonitorLog;
    }

    private void OnDisable()
    {
        LB_Logger.Instance.PrintLogEvent -= MonitorLog;
    }

    private void MonitorLog(string log)
    {
        LogTextContainer.text = log;
    }
}

Log Monitörümüz, LB_Logger’a yollanan logları ekranda göstermeye yarıyor. Ne zaman aktif olsa LB_Logger’ın PrintLogEvent olayını dinliyor. Ne zaman deaktif olursa da dinlediği bu olaydan kendini siliyor.

Adım 4 — Logları kaydetmek ya da kaybetmek, işte bütün mesele bu.

Logları kaydetmemiz sebebini bir türlü bulamadığımız hatalara karşı bizi koruyabilir. Kaydedilen loglar çok daha rahat izlenirler ve dolayısıyla hataların bulunmasını kolaylaştırırlar. LB_LogSaver’ımız da bizim için logları kaydedecek.

using System;
using System.IO;
using UnityEngine;
public class LB_LogSaver : MonoBehaviour {
    private string logFilePath;
    private string logHistory;

    private void Start()
    {
        SetDataPath();
        LoadLogHistory();
    }

    private void SetDataPath()
    {
        #if UNITY_EDITOR
            logFilePath = Application.dataPath + "/LB_Logger_LogFile.txt";
        #else
            logFilePath = Application.persistentDataPath + "/LB_Logger_LogFile.txt";
        #endif
    }

    private void LoadLogHistory()
    {
        using (FileStream fs = new FileStream(logFilePath, FileMode.Open))
        {
            using (StreamReader reader = new StreamReader(fs))
            {
                logHistory = reader.ReadToEnd();
            }
        }
    }

    private void OnDestroy()
    {
        using (FileStream fs = new FileStream(logFilePath, FileMode.Create))
        {
            using (StreamWriter writer = new StreamWriter(fs))
            {
                logHistory += DateTime.Now;
                logHistory += Environment.NewLine + "----------------" + Environment.NewLine;
                logHistory += LB_Logger.Instance.GetLogString();
                logHistory += Environment.NewLine + "----------------" + Environment.NewLine;
                writer.Write(logHistory);
            }
        }
    }
}

Singleton hakkında okumaya devam etmek isteyenler için önerilerim.

Şurası da şöyle olsa daha iyi olurdu demekten, bunu yorum olarak belirtmekten lütfen çekinmeyin. Bana cosgun.halil@gmail.com adresinden ulaşmak konusunda da rahat hissetmenizi rica ediyorum.

Eğer bu çalışmam işinize yaradıysa ve daha fazla çalışma yapabilmem için bana destek olmak isterseniz, bir kahvenizi içerim. 🙂

Saygılar.

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.