Bugün yine MVVM (Model-View-ViewModel) ile ilgili bir yazı ile karşınızdayım. Bir önceki yazıda MVVM Light Toolkit’in Messenger servisini kullanarak ViewModel’ler arasındaki iletişimin nasıl sağlandığına göz atmıştık. Bugün ise bu iletişimi sağlayan Messenger servisini kendimiz geliştiriyor olacağız.

Messenger Servisi UML Diyagramı

Servisimizin sağlayacağı fonksiyonellikler aşağıdaki gibi olacak:

public interface IMessenger  
{
    void Register<T>(Action<T> action);
    void Unregister<T>(Action<T> action);
    void Send<T>(T message);
}

Tanımlamış olduğumuz interface’e baktığımızda servisimizin, bir mesaja abone olmak için Register, abonelikten çıkmak için Unregister ve bir mesajı yayın yapmak için de Send metodunu tanımlaması gerektiğini görmekteyiz. Interface’imizi tanımladığımıza göre artık Messenger servisimizi geliştirmeye başlayabiliriz. Projemize hemen Messenger adında yeni bir sınıf ekliyoruz ve bu sınıfa IMessenger arayüzümüzü implemente ediyoruz.

public class Messenger : IMessenger  
{
    public void Register<T>(Action<T> action)
    {
        throw new NotImplementedException();
    }

    public void Unregister<T>(Action<T> action)
    {
        throw new NotImplementedException();
    }

    public void Send<T>(T message)
    {
        throw new NotImplementedException();
    }
}

Servisimizin bir şekilde abone listesini tutması gerekiyor ki yayın yapıldığında ilgili abonelere mesaj iletilebilsin. Bu yüzden servisimizde abone listesini tutabileceğimiz bir Dictionary yapısına ihtiyacımız olacak.

private static Dictionary<Type, List<object>> subscribers = new Dictionary<Type, List<object>>();  

Dictionary yapısına baktığımızda ilgili tip ve o tipin abonelerinin bulunduğu bir yapımız mevcut. Key olarak Type ve value olarak da o tipe ait olan Action listesini içermektedir.

public void Register<T>(Action<T> action)  
{
    lock (myLock)
    {
        if (subscribers.ContainsKey(typeof(T)))
        {
            var actions = subscribers[typeof(T)];
            actions.Add(action);
        }
        else
        {
            var actions = new List<object>();
            actions.Add(action);
            subscribers.Add(typeof(T), actions);
        }
    }
}

Register metodunu incelediğimizde ilk olarak abone listemizin belirtilen tipte bir tipi içerip içermediği kontrol ediliyor. Eğer içeriyorsa parametre olarak gelen Action, ilgili tipin abone listesine ekleniyor. İçermiyorsa da Dictionary yapısında yeni bir tip oluşturulup ilgili Action’ı ile beraber abone listesine ekleniyor.

public void Unregister<T>(Action<T> action)  
{
    lock (myLock)
    {
        if (subscribers.ContainsKey(typeof(T)))
        {
            var actions = subscribers[typeof(T)];
            actions.Remove(action);
            if (actions.Count == 0)
            {
                subscribers.Remove(typeof(T));
            }
        }
    }
}

Unregister metoduna baktığımızda yine aynı şekilde gelen tipin abone listesinde olup olmadığı kontrol ediliyor, eğer varsa ilgili Action listeden çıkarılıyor. Aynı zamanda belirtilen tipin tetikleyeceği bir Action yoksa belirtilen tip abone listesinden çıkarılıyor.

public void Send<T>(T message)  
{
    if (subscribers.ContainsKey(typeof(T)))
    {
        var actions = subscribers[typeof(T)];
        foreach (Action<T> action in actions)
        {
            action.Invoke(message);
        }
    }
}

Send metodumuzu da yukarıdaki gibi tanımlıyoruz. Burada belirtilen tipin abone listesinde olup olmadığını kontrol ediyoruz. Eğer belirtilen tip, abone listesinde varsa ona bağlı olan her bir Action’ın, Invoke metodu çalıştırılarak ilgili mesaj tüm abonelere iletiliyor.

Buraya kadar Messenger servisimizin gerçekleştirmesi gereken temel işlevleri tanımlamış olduk, fakat biraz daha geliştirmemiz gereken noktalar da yok değil. Dikkat ettiyseniz bazı metodlarda lock anahtar kelimesini kullandık. Örneğin Send metodunda foreach içerisinde bir işlem yapılırken aynı anda farklı bir thread tarafından yeni bir Register işlemi gerçekleşirse aynı kaynağa erişmeye çalıştıkları için deadlock meydana gelecektir. Bunu engellemek için de lock anahtar kelimesini kullanarak o nesne üzerinde işlem yapılırken başka threadler tarafından kullanılmamasını garanti etmiş oluyoruz.

Bir diğer önemli nokta da Messenger servisimizin uygulama düzeyinde erişilebilir olmasıdır. Bu yüzden singleton bir yapıya sahip olması gerekmektedir. Çünkü farklı sınıflardan Messenger servisine erişildiğinde her birinin aynı nesne üzerinde işlem yapıyor olması gerekiyor. Bundan dolayı Messenger nesnesinin oluşturulması işlemini kontrol altına almamızda fayda var. İlk olarak yapıcı metodumuzun public olan erişim belirleyicisini dışarıdan yeni bir nesne oluşturulmasını engellemek amacıyla private olarak değiştiriyoruz. Yapıcı metodumuzu private olarak değiştirdiğimiz için bir şekilde Messenger sınıfımızı dışarıya açmamız gerekiyor. Bunun için de statik bir property tanımlaması yaparak önceden oluşturduğumuz Messenger nesnemizi dışarıya açıyoruz. Son olarak da sealed anahtar kelimesini kullanarak sınıfımızdan kalıtım alınmasını engelliyoruz ve voila! Kısaca ViewModel’ler arasında veri alışverişi sağlayan bir sınıfın basit bir şekilde nasıl geliştirildiğini görmüş olduk.

About the Author