Skip to content

Observer Design Pattern (Tasarım Deseni) Nedir?

Merhaba, tasarım desenleri üzerine yazdığım blog serisinin bu bölümünde Observer tasarım desenini anlatacağım. Projelerimizde bazı senaryolarda, bir sınıfa farklı türden sınıflar bağımlı olabilmektedir (One to many ilişkisi). Böyle bir senaryoda, projelerin kolay bir şekilde değişime açık ve sürdürülebilir olabilmesi için ana sınıf içerisinde diğer sınıfların somut halde kullanılmaması, bu bağımlılıkların sıkı bir şekilde olmaması gerekir. Kısaca Observer tasarım deseni, projelerimizde istediğimiz esnekliği sağlamak için bu bağımlılıkları soyut hale getirmektir. Örnek uygulama üzerinden yaklaşımın ne olduğunu ve neden uygulamamız gerektiğini daha iyi anlamaya çalışalım.

Observer Tasarım Deseninin Kullanılmadığı Uygulama

Makale yayınları yapılan bir uygulama, bu uygulamayı ücretli ve ücretsiz kullanan üyeler olduğunu varsayalım. Uygulamada, ücretsiz kullanan üyelere makalelere ait bildirimler özet şeklinde, ücretli üyelere ise bildirimlerin makalelere ait tüm verileri kapsayacak şekilde gönderilmesi gerekmektedir. Uygulama verisi ve bildirim gösterimi yapacak sınıflar arasındaki ilişki ve uygulamanın kodları aşağıdadır.

Makale Verisi ve Bildirim Sınıfları İlişkisi
    class ArticleData
    {
        string  author,title, content;
        DateTime date;
        AllArticleDisplay allArticleDisplay;
        SummaryArticleDisplay summaryArticleDisplay;
        public ArticleData(AllArticleDisplay allArticleDisplay, SummaryArticleDisplay summaryArticleDisplay)
        {
            this.allArticleDisplay = allArticleDisplay;
            this.summaryArticleDisplay = summaryArticleDisplay;
        }
        public string GetArticleTitle()
        {
            return "Article Title";
        }
        public string GetArticleContent()
        {
            return "News Article Content News Article Content News Article Content";
        }
        public string GetArticleAuthor()
        {
            return "Author Name";
        }
        public DateTime GetArticleDate()
        {
            DateTime t = DateTime.Now;
            return t;
        }
        public void DataChanged()
        {
            title = GetArticleTitle();
            content = GetArticleContent();
            author = GetArticleAuthor();
            date = GetArticleDate();

            allArticleDisplay.Update(title, content, author, date);
            summaryArticleDisplay.Update(title, content, author, date);
        }
    }
    class AllArticleDisplay
    {
        string title, content, type;
        DateTime date;
        public void Update(string title, string content, string type, DateTime date)
        {
            this.title = title;
            this.content = content;
            this.type = type;
            this.date = date;
            Display();
        }
        public void Display()
        {
            Console.WriteLine("Title: " + title + "Content: " + content + "type: " + type + "Date:" + date);
        }
    }
    class SummaryArticleDisplay
    {
        string summary;
        public void Update(string title, string content, string author, DateTime date)
        {
            this.summary = title + " "  +content.Substring(0, 15) + " " + author +" " + date;
            Display();
        }
        public void Display()
        {
            Console.WriteLine("\nSummary:{0}",summary);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            AllArticleDisplay allArticleDisplay = new AllArticleDisplay();
            SummaryArticleDisplay summaryArticleDisplay = new SummaryArticleDisplay();
            ArticleData articleData = new ArticleData(allArticleDisplay, summaryArticleDisplay);
            articleData.DataChanged();
        }
    }

Uygulamanın getirmiş olduğu dezavantajlar şunlardır;

  • ArticleData sınıfında, veri değişiminde, bildirim sınıfları AllArticleDisplay ve SummaryArticleDisplay’in Update metotu çağrılmakta ve bunun için bu sınıfların somut halde burada kullanılmasına gerek yoktur. Bu durum esnekliği kısıtlar.
  • İlerleyen zamanlarda şu anda uygulamada var olan özet ve tüm makale bildirimlerinin dışında üçüncü bir bildiri türünün eklenmek istemesi farklı bir display (bildiri) sınıf ihtiyacını ortaya çıkaracak, bu doğrultuda ArticleData sınıfında değişiklik yapmak gerekecektir.
  • ArticleData constructor’ı bildirim sınıflarını parametre aldığı için ana program içerisinde ArticleData nesnesini üretirken iki display nesnesini parametre olarak geçme zorunluluğu bir problemdir.
  • Display nesnelerinin istenen durumda program içerisinde, ArticleData bildirimlerini alma ya da almama özelliğine sahip olmaları gerekmektedir.

Sayabildiklerim ve benzeri problemleri ortadan kaldırmak ve projemizi ihtiyaçlara göre daha sürdürülebilir hale getirmek için Observer tasarım desenini kullanmalıyız.

Observer Tasarım Deseninin Uygulanması

İlk olarak karşımıza iki kavram ortaya çıkmaktadır. Bunlar; observer ve subject. Subject, uygulama verileri ile ilgili metotları içerisinde barındıran, diğer sınıfların bağımlı olduğu ana sınıfta implemente edilen interface yapısıdır. Observer ise, veriler değiştikçe bildirimleri takip edecek bildirim sınıflarında implemente edilecek interface yapısıdır. Uygulamanın Observer tasarım deseni ile düzenlenmesi sonucu elde edilecek UML diyagramı ve uygulama kodu aşağıdadır.

Observer Tasarım Deseni Kullanılan Uygulamanın UML Diyagramı
    interface ISubject
    {
        public void AddObserver(IObserver o);
        public void RemoveObserver(IObserver o);
        public void NotifyObservers();
    }
    class ArticleData : ISubject
    {
        string author, title, content;
        DateTime date;
        List<IObserver> observerList;
        public ArticleData()
        {
            observerList = new List<IObserver>();
        }
        public void AddObserver(IObserver observer)
        {
            observerList.Add(observer);
        }
        public void NotifyObservers()
        {
            foreach (var observer in observerList)
            {
                observer.Update(title, content, author, date);
            }
        }
        public void RemoveObserver(IObserver observer)
        {
            observerList.Remove(observer);
        }
        public string GetArticleTitle()
        {
            return "Article Title";
        }
        public string GetArticleContent()
        {
            return "Summary Article Content Article Content Article Content";
        }
        public string GetArticleAuthor()
        {
            return "Author Name";
        }
        public DateTime GetArticleDate()
        {
            return DateTime.Now;
        }
        public void DataChanged()
        {
            title = GetArticleTitle();
            content = GetArticleContent();
            author = GetArticleAuthor();
            date = GetArticleDate();
            NotifyObservers();
        }
    }
    interface IObserver
    {
        public void Update(string title, string content, string author, DateTime date);
    }
    class AllArticleDisplay : IObserver
    {
        
        string author, title, content;
        DateTime date;
        public void Update(string title, string content, string author, DateTime date)
        {
            this.title = title;
            this.content = content;
            this.author = author;
            this.date = date;
            Display();
        }
        public void Display()
        {
            Console.WriteLine("\nTitle:{0}\nContent:{1}\nAuthor:{2}\nDate:{3}", title, content, author, date);
        }
    }
    class SummaryArticleDisplay : IObserver
    {
        string summary;
        public void Update(string title, string content, string author, DateTime date)
        {
            this.summary = title + " "  +content.Substring(0, 15) + " " + author +" " + date;
            Display();
        }
        public void Display()
        {
            Console.WriteLine("\nSummary:{0}",summary);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            AllArticleDisplay allArticleDisplay = new AllArticleDisplay();
            SummaryArticleDisplay summaryArticleDisplay = new SummaryArticleDisplay();
            ArticleData article = new ArticleData();
            Console.WriteLine("İlk işlem:");
            article.AddObserver(allArticleDisplay);
            article.AddObserver(summaryArticleDisplay);
            article.DataChanged();
            Console.WriteLine("\nİkinci işlem:");
            article.RemoveObserver(summaryArticleDisplay);
            article.DataChanged();
        }
    }

Tasarım deseninin projeye uygulanması ile birlikte, mevcut olan dezavantajlar giderilmiş olup, proje değişimlere karşı daha esnek hale getirilmiştir. IObserver interface’ini implemente etmiş olan display sınıflarından oluşan nesneler, istenildiğinde observer listesine eklenip çıkarılır. Ayrıca, ihtiyaç halinde oluşturulacak yeni tür display sınıfında ArticleData sınıfında değişiklik yapılmayacaktır. Uygulamanın çıktısı aşağıda olup, ikinci işlemde summaryArticleDisplay nesnesi observer listesinden çıkarıldığı için tekrar summary bildiriminin alınmadığı görünmektedir.

Uygulama Ekran Çıktısı

Kaynaklar kısmında belirttiğim makaleleri inceleyip, kendimce oluşturduğum bir senaryo üzerinden Observer tasarım desenini size aktarmaya çalıştım . Benim için verimli bir çalışma oldu diyebilirim. Umarım konu hakkında bilgilenmek isteyenlere de faydalı bir paylaşım olmuştur. Benimle yasinatilgan60@gmail.com adresinden iletişime geçebilirsiniz. İyi günler.

Kaynaklar

https://www.geeksforgeeks.org/observer-pattern-set-1-introduction/

https://www.geeksforgeeks.org/observer-pattern-set-2-implementation/

Published inC#Design PatternsOOP

Comments are closed.

Yasin Atılkan © 2025 Copyright | Author WordPress Theme by Compete Themes