C# Iterator Design Pattern(Iterator Tasarım Deseni)

Merhaba,

Behavioral Patterns(Davranışsal Kalıplar) kategorisinde bulunan Iterator Design Pattern üzerine içerik oluşturacağız. Iterator Türkçe olarak anımsattığı iterasyon kelimesine karşı gelmektedir. İterasyon dediğimiz yapı bir çözüme giden her adımı tarif etmektedir. Kelimenin mahiyetiyle ilgili daha detaylı bilgiyi C#’ta Iterator Kavramı ve yield Anahtar Sözcüğü başlıklı makaleden edinebilirsiniz.

Yukarıda yaptığımız girişe nazaran iterasyonu farklı bir açıdan da ele almak gerekirse eğer “tekrarlayıcı” olarak tarif etmek yerinde olacaktır. Belirli işlevleri, belirli şartlar altında tekrarlayarak tıpkı bir döngü misali işlev yapan yapıya biz yazılım terminolojisinde iterasyon diyoruz. Yazılım terminolojisi ve iterasyon demişken direkt olarak benim aklıma foreach döngüsü gelmektedir.

foreach döngüsü, iterasyon mantığıyla çalışan bir mekanizmadır. Haliyle bu mekanizmanın kullandığı kaynak niteliğindeki yapıları genel olarak koleksiyonlar ve diziler olarak düşünebiliriz. Bu yapıların kalıtımsal durumlarına dikkat edersek eğer IEnumerable interface’inden türemekteler ve bu tip sayesinde ortak bir noktada erişilebilir hale gelmektedirler. Ayrıca IEnumerable tipi kalıtım verdiği sınıfa iterasyon özelliği kazandırmaktadır.

İşte bu makalemizde yukarıda sizlere sunmuş olduğum iterasyon mantık ve yapısında oluşturulan tasarım deseni üzerine teknik olarak konuşacağız.

Peki Iterator Tasarım Deseni Nerelerde Kullanılır?

Bir veri kümesini uygulamamızda diğer kısımlar ile olan bağlantısını en aza indirmek için; listede yer alan nesnelerin, sırasıyla uygulamadan soyutlanması amacıyla kullanılır. Yani yapılacak olan işlemdeki nesneler ile olan tüm kontroller iterasyon deseninde gerçekleştirilir ve veri kümesi üzerinde bu iterasyonun kuralları çerçevesinde bir döngü söz konusu olur.

Şimdi gelin bu desende kullanacağımız yapıları inceleyelim.

Öncelikle sizler aşağıdaki UML şemasına göz atarken bende kullanacağımız yapılara izahat getireyim…
C# Iterator Design Pattern(Iterator Tasarım Deseni)

  • Aggregate
    Veri kümesi içerisinde dolaşmak için bir IIterator interface’i tipinden Iterator yaratılmasını zorunlu tutan arayüzdür.
  • Iterator
    Veri kümesi içerisinde dolaşmanın tüm şart ve imzasını bu arayüz belirlemektedir. Yani bir enumerator(sayıcı) görevi üstlenmektedir. Uzun lafın kısası, elimizdeki veri kümesi üzerinde döngü esnasında verileri/nesneleri elde edebilmemiz için gerekli işlemleri/kontrolleri/şartları/hususları tanımlar.
  • ConcreteAggregate
    Veri kümesini barındıran nesnedir. Aggregate arayüzünü uygulayarak Iterator nesnesini oluşturur.
  • ConcreteIterator
    Iterator arayüzünü uygulayan ve içerisinde iterasyon metot ve özelliklerini barındıran, yukarıda da bahsettiğimiz enumerator görevini üstlenen sınıftır.
  • Client
    Deseni kullanarak, belirtilen şartlara göre veri kümesi içerisindeki elemanlara erişen nesnemizdir.

Evet… Sıra geldi bu yapıları kullanarak belli başlı senaryoları gerçekleştirmeye. Bu içeriğimizde, belirli bir veri kümesi üzerinde periyodik olarak(ki periyodunu kendimizin belirlediği) bir döngü ve verilen iki tarih arasındaki tüm hafta sonlarını hesaplayarak bizlere getiren bir uygulamak oluşturmak suretiyle iki farklı örnek senaryo üzerinde çalışıp konumuzu noktalandıracağız.

1. Senaryo

Öncelikle yukarıda da belirttiğimiz gibi bu senaryomuzda periyodik olarak belirli kurallar çerçevesinde tekrarlayan bir döngü üzerinde çalışacağız.

İlk olarak veri kümesinde kullanacağımız entity yapısını oluşturalım. Ben örnek olarak “Personel” isminde bir struct oluşturuyorum.

    struct Personel
    {
        public int Id { get; set; }
        public string Adi { get; set; }
        public string SoyAdi { get; set; }
    }

Ardından Aggregate arayüzünü oluşturalım.

    interface IAggregate
    {
        IIterator CreateIterator();
    }

Dikkat ederseniz IAggregate arayüzümüz içerisinde geriye IIterator dönen CreateIterator metodu mevcuttur. Haliyle IIterator arayüzünü oluşturalım.

    interface IIterator
    {
        //Bir sonraki adımda eleman var mı?
        bool HasItem();
        //Bir sonraki adımdaki elemanı getir.
        Personel NextItem();
        //Mevcut elemanı getir.
        Personel CurrentItem();
    }

Görüldüğü üzere Iterator arayüzünde iterasyonda kullanacağımız metot ve özellikler mevcuttur.

Arayüzleri oluşturduktan sonra sıra bu arayüzlerden türeyen Concrete(somut) sınıflara geldi. Öncelikle ConcreteAggregate sınıfımızı oluşturalım.

    class PersonelAggregate : IAggregate
    {
        List<Personel> PersonelListesi = new List<Personel>();
        public void Add(Personel Model) => PersonelListesi.Add(Model);
        public Personel GetItem(int index) => PersonelListesi[index];
        public int Count { get => PersonelListesi.Count; }
        public IIterator CreateIterator() => new PersonelIterator(this);
    }

Yukarıdaki kod bloğuna göz atarsanız eğer “PersonelAggregate” sınıfı aslında bir ConcreteAggregate sınıfıdır. İçeriğinde IAggregate arayüzünün uygulattığı geriye “PersonelIterator” nesnesi dönen CreateIterator metodunun dışında, veri kümesini barındırmakta ve veri kümesiyle ilgili ekleme, getirme vs. gibi ek işlemleri gerçekleştirmektedir.

Şimdide sıra ConcreteIterator sınıfını oluşturmaya geldi. Tabi bu sınıfımız yukarıda da gördüğünüz gibi “PersonelIterator” sınıfı olacaktır.

        class PersonelIterator : IIterator
        {
            PersonelAggregate aggregate;
            int currentindex;
            public PersonelIterator(PersonelAggregate aggregate) => this.aggregate = aggregate;
            public Personel CurrentItem() => aggregate.GetItem(currentindex);
            public bool HasItem()
            {
                if (currentindex < aggregate.Count)
                    return true;
                return false;
            }
            public Personel NextItem()
            {
                if (HasItem())
                    return aggregate.GetItem(currentindex++);
                return new Personel();
            }
        }

Evet… Dikkat ederseniz “PersonelIterator” sınıfımızda iterasyon kurallarının sınırlarını belirlemiş olduk. Burada periyodumuz 1’er 1’er olarak tanımlanmıştır. Tabi komutların algoritmik izahatine fazla girmeyeceğim. Analizi anlamak ve yorumlamak sizlere kalmış.

Şimdi inşa ettiğimiz bu yapıyı kullanalım.

        static void Main(string[] args)
        {
            PersonelAggregate aggregate = new PersonelAggregate();
            aggregate.Add(new Personel { Id = 1, Adi = "Gençay", SoyAdi = "Yıldız" });
            aggregate.Add(new Personel { Id = 2, Adi = "Ahmet", SoyAdi = "Çakmak" });
            aggregate.Add(new Personel { Id = 3, Adi = "Mehmet", SoyAdi = "Aslıbay" });
            aggregate.Add(new Personel { Id = 4, Adi = "Ayşe", SoyAdi = "Solmaz" });
            aggregate.Add(new Personel { Id = 5, Adi = "Fatma", SoyAdi = "Nurgül" });

            IIterator iterasyon = aggregate.CreateIterator();
            while (iterasyon.HasItem())
            {
                Console.WriteLine($"ID : {iterasyon.CurrentItem().Id}\nAdı : {iterasyon.CurrentItem().Adi}\nSoyadı : {iterasyon.CurrentItem().SoyAdi}\n*****");
                iterasyon.NextItem();
            }

            Console.Read();
        }

Projemizi derleyip çalıştırdığımızda aşağıdaki sonuçla karşılaşmaktayız.
C# Iterator Design Pattern(Iterator Tasarım Deseni)

Gördüğünüz gibi Iterator tasarım deseni sayesinde belirli bir periyotta döngüsel işlem gerçekleştirmiş olduk. Şimdi sıra 2. senaryoya gelmiş bulunmaktadır.

2. Senaryo

2. senaryomuzda ise verilen iki tarih arasındaki hafta sonlarını yazdıracağız. Burada da belirli periyotta bir işlem yapılması söz konusu olduğundan dolayı iterasyonel bir durum vardır diyebiliriz. Haliyle buyrun Iterator tasarım deseniyle bu işlemi nasıl yapacağımızı beraber icra edelim.

Öncelikle Aggregate arayüzümüzü oluşturalım.

    interface IAggregate
    {
        IIterator CreateIterator();
    }

Ardından Iterator arayüzümüzü oluşturalım.

    interface IIterator
    {
        bool HasDate();
        DateTime CurrentDate();
    }

Şimdide Concrete nesnelerimize geçebiliriz.

Öncelikle ConcreteAggregate nesnemizi oluşturalım.

    class DateTimeAggregate : IAggregate
    {
        public DateTime startDate;
        public DateTime endDate;
        public IIterator CreateIterator() => new DateTimeIterator(this);
    }

Ve ardından ConcreteIterator nesnemizi oluşturalım.

    class DateTimeIterator : IIterator
    {
        DateTimeAggregate aggregate;
        DateTime currentDate;
        public DateTimeIterator(DateTimeAggregate aggregate)
        {
            this.aggregate = aggregate;
            currentDate = aggregate.startDate;
        }
        public DateTime CurrentDate() => currentDate;
        public bool HasDate()
        {
            if (currentDate.DayOfWeek == DayOfWeek.Saturday || currentDate.DayOfWeek == DayOfWeek.Sunday)
            {
                int dayCount = currentDate.DayOfWeek == DayOfWeek.Saturday ? 1 : 6;
                currentDate = currentDate.AddDays(dayCount);
            }
            else
            {
                int dayCount = (int)currentDate.DayOfWeek;
                currentDate = currentDate.AddDays(6 - dayCount);
                /*6'dan ilgili günün haftalık sayısını çıkarırsak eğer 
                 Cumartesi gününe kalan günü hesaplamış oluruz.
                 Haliyle bu hesabı ilgili tarihe eklersek eğer
                 o haftanın hafta sonuna ulaşmış oluruz.*/
            }
            return currentDate < aggregate.endDate;
        }
    }

Yapmış olduğumuz bu tasarıyı kullanalım.

        static void Main(string[] args)
        {
            DateTimeAggregate tarih = new DateTimeAggregate();
            tarih.startDate = new DateTime(2017, 01, 01);
            tarih.endDate = DateTime.Now;
            IIterator iterator = tarih.CreateIterator();
            while (iterator.HasDate())
            {
                Console.WriteLine(iterator.CurrentDate());
            }

            Console.Read();
        }

Projemizi derleyip çalıştırdığımız zaman aşağıdaki ekran görüntüsünde olduğu gibi bir sonuçla karşılaşmaktayız.
C# Iterator Design Pattern(Iterator Tasarım Deseni)
Gördüğünüz gibi verdiğimiz iki tarih arasındaki hafta sonlarını Iterator tasarım deseniyle hesaplamış olduk.

Iterator Design Pattern sayesinde periyodik yahut aşamalı işlemleri çok rahat bir şekilde ve veri kümesini uygulamadan soyutlarak kullanabilme olanağı bulmuş oluyoruz.

Eğer ki Iterator Design Pattern’i gerçek uygulamalarınızda kullanırsanız; gelecekte, bu gün kullandığınız [koleksiyon/array/dizi/veri kümeleri]’nden daha performanslı yapılar .NET kütüphanesine dahil edildiğinde bu yapılarla mevcutları değiştirmek istediğinizde uygulamadan bağımsızlaştırılmış ve soyutlanmış olduklarından dolayı sadece ilgili kaynaklardaki tanımları değiştirmeniz yeterli olacaktır. Aksi taktirde projede kaynaklarla alakalı tüm noktalara müdahale etmeniz gerekecek, haliyle SOLID prensiplerine uygun olmayacak tarzda bir gelişimden ziyade değişim söz konusu olacaktır.

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar diliyorum…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

Copy Protected by Chetan's WP-Copyprotect.