Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

C# Design Patterns Overview

C#'da nesne yönelimli tasarım desenleri, yazılım geliştirme sürecinde yaygın olarak kullanılan çözüm örüntüleridir. Bu desenler, tekrar eden problemleri çözmek için kullanılan genel yaklaşımları ifade eder ve yazılımın esnekliğini, okunabilirliğini ve yeniden kullanılabilirliğini artırır.


C# Dilinde Nesne Yönelimli Tasarım Şablonları

C#’da nesne yönelimli tasarım desenleri, yazılım geliştirme sürecinde yaygın olarak kullanılan çözüm örüntüleridir. Bu desenler, tekrar eden problemleri çözmek için kullanılan genel yaklaşımları ifade eder ve yazılımın esnekliğini, okunabilirliğini ve yeniden kullanılabilirliğini artırır.

Singleton Pattern (Tekillik Deseni): Tekillik deseni, bir sınıfın yalnızca bir örneğinin oluşturulmasını ve bu örneğe global bir erişim sağlanmasını sağlar. Bu desen genellikle kaynak tüketimini azaltmak veya paylaşılan bir kaynağa tek bir erişim noktası sağlamak için kullanılır. Örneğin:

public class Singleton
{
    private static Singleton instance;
    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

Factory Method Pattern (Fabrika Metodu Deseni): Fabrika metodu deseni, bir süper sınıfın alt sınıflarının nasıl oluşturulacağını belirleyen bir yöntem sağlar. Bu desen, bir nesne oluşturmak için genel bir arayüz sağlar, ancak alt sınıfların hangi sınıfların oluşturulacağını belirlemesine izin verir. Örneğin:

public abstract class Creator
{
    public abstract Product FactoryMethod();
}

public class ConcreteCreator : Creator
{
    public override Product FactoryMethod()
    {
        return new ConcreteProduct();
    }
}

Observer Pattern (Gözlemci Deseni): Gözlemci deseni, bir nesne durumunda değişiklik olduğunda bağımlı nesnelerin otomatik olarak güncellenmesini sağlar. Bu desen, birçok yayın/abone senaryosunda kullanılır. Örneğin:

public interface IObserver
{
    void Update(ISubject subject);
}

public interface ISubject
{
    void Attach(IObserver observer);
    void Detach(IObserver observer);
    void Notify();
}

public class ConcreteSubject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();

    public void Attach(IObserver observer)
    {
        observers.Add(observer);
    }

    public void Detach(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void Notify()
    {
        foreach (var observer in observers)
        {
            observer.Update(this);
        }
    }
}

Decorator Pattern (Dekoratör Deseni): Dekoratör deseni, bir nesneyi dinamik olarak genişletmek için kullanılır. Bu desen, sınıfın alt sınıflarını çok katmanlı bir şekilde genişletmeyi sağlar ve nesneye dinamik olarak davranış eklemeyi mümkün kılar. Örneğin:

public abstract class Pizza
{
    public abstract string GetDescription();
    public abstract double GetCost();
}

public class Margherita : Pizza
{
    public override string GetDescription()
    {
        return "Margherita Pizza";
    }

    public override double GetCost()
    {
        return 6.99;
    }
}

public abstract class PizzaDecorator : Pizza
{
    protected Pizza _pizza;
    public PizzaDecorator(Pizza pizza)
    {
        _pizza = pizza;
    }

    public override string GetDescription()
    {
        return _pizza.GetDescription();
    }

    public override double GetCost()
    {
        return _pizza.GetCost();
    }
}

public class Cheese : PizzaDecorator
{
    public Cheese(Pizza pizza) : base(pizza)
    {
    }

    public override string GetDescription()
    {
        return _pizza.GetDescription() + ", Cheese";
    }

    public override double GetCost()
    {
        return _pizza.GetCost() + 1.5;
    }
}

Bu örnekler, C#’da sıkça kullanılan bazı temel nesne yönelimli tasarım desenlerini kapsamaktadır. Tasarım desenleri, yazılım geliştirme sürecinde çeşitli problemleri çözmek için güçlü araçlardır ve doğru şekilde kullanıldığında, kodunuzun daha okunabilir, esnek ve yeniden kullanılabilir olmasını sağlar.


Bilmeniz Gerekenler

“Tasarım Desenleri”, yazılım geliştirme sürecinde sıkça karşılaşılan problemleri çözmek için geliştirilmiş genel çözüm yaklaşımlarıdır. Bu desenler, deneyimli yazılım geliştiricileri tarafından yıllar boyunca geliştirilmiş ve belirli sorunları çözmek için etkili olduğu kanıtlanmıştır.

Tasarım desenleri hakkında derinlemesine bilgi sahibi olmak, bir yazılım geliştiricisi için son derece önemlidir.

  1. Tasarım Desenlerinin Avantajları:
    • Kodun Yeniden Kullanılabilirliği: Tasarım desenleri, tekrar eden problemleri çözmek için genel çözüm yolları sunar, böylece kodunuzun yeniden kullanılabilirliğini artırır.
    • Esneklik: Desenler, kodunuzu daha esnek hale getirerek değişikliklere daha iyi uyum sağlar. Kodunuzu daha kolay genişletebilir ve değiştirebilirsiniz.
    • Okunabilirlik: Tasarım desenleri, kodunuzun daha anlaşılır olmasını sağlar. Belirli bir deseni kullanan başka bir geliştirici, kodunuzu daha kolay anlayabilir.
  2. Kategoriler ve Alt Kategoriler:
    • Creational Patterns (Yaratımsal Desenler): Nesnelerin nasıl oluşturulacağına ve birleştirileceğine odaklanır. Örnekler arasında Singleton, Factory Method ve Abstract Factory bulunur.
    • Structural Patterns (Yapısal Desenler): Sınıfların ve nesnelerin nasıl bir araya getirileceğine ve yapılandırılacağına odaklanır. Örnekler arasında Adapter, Decorator ve Proxy bulunur.
    • Behavioral Patterns (Davranışsal Desenler): Nesnelerin nasıl birlikte çalışacağına ve bilgi alışverişi yapacağına odaklanır. Örnekler arasında Observer, Strategy ve Command bulunur.
  3. Uygulama Alanları:
    • Tasarım desenleri, farklı uygulama alanlarında kullanılabilir. Web geliştirme, mobil uygulama geliştirme, oyun geliştirme ve masaüstü uygulamalar gibi çeşitli alanlarda tasarım desenlerinin kullanımı yaygındır.
    • Her desenin belirli bir kullanım alanı ve avantajları vardır. Örneğin, MVC (Model-View-Controller) deseni genellikle web uygulamalarında kullanılırken, Command deseni, işlemleri nesnelere dönüştürmek için kullanılır.
  4. Gerçek Hayat Örnekleri:
    • Tasarım desenlerinin gerçek dünya örneklerini incelemek, bu desenleri anlamanın ve kullanmanın en iyi yollarından biridir. Gerçek dünya problemlerini çözmek için tasarım desenlerinin nasıl kullanılabileceğini gösteren örnekler, tasarım desenlerini öğrenmenin ve uygulamanın etkili bir yoludur.
  5. Anti-Patterns (Karşı Desenler):
    • Tasarım desenleriyle birlikte, karşı desenlerin (anti-patterns) de anlaşılması önemlidir. Anti-patterns, yaygın hatalı veya suboptimal yazılım tasarım pratiklerini tanımlar. Bu desenlerin bilinmesi, kötü tasarımlardan kaçınmanıza ve daha iyi bir kod üretmenize yardımcı olur.

Tasarım desenlerini derinlemesine anlamak, yazılım geliştirme sürecinizde daha iyi kararlar vermenize, kodunuzu daha iyi organize etmenize ve daha iyi bir yazılım mimarisi oluşturmanıza yardımcı olur. Bu nedenle, C# programlama dilinde tasarım desenlerine odaklanarak, yazılım geliştirme becerilerinizi geliştirebilir ve daha etkili yazılım üretebilirsiniz.

Yazılım Tasarım Şablonlarını Tanımlama

Object-oriented programming

Nesne yönelimli programlama (OOP), yazılım geliştirme sürecinde kullanılan bir programlama paradigmasıdır. C# gibi dillerde yaygın olarak kullanılan bir yaklaşımdır. OOP’nin temel amacı, yazılımı nesnelerin bir araya gelmesi ve etkileşimi üzerine kurmaktır.

  1. Sınıflar ve Nesneler:
    • Sınıflar, bir nesnenin özelliklerini (alanlar) ve davranışlarını (metodlar) tanımlayan bir yapıdır. Örneğin, bir “Araba” sınıfı, arabaların özelliklerini (marka, model, renk vb.) ve davranışlarını (sürme, durma, dönme vb.) tanımlar.
    • Nesneler, sınıflardan oluşturulan örneklerdir. Bir sınıftan birden fazla nesne oluşturulabilir ve her bir nesne kendi özelliklerine ve davranışlarına sahiptir.
  2. Encapsulation (Kapsülleme):
    • Encapsulation, verilerin ve bu verilere erişim yöntemlerinin bir arada tutulmasıdır. Sınıflar, kendi içlerinde verileri ve bu verilere erişmek için yöntemleri kapsüller.
    • Erişim belirleyicileri (public, private, protected gibi) kullanılarak, sınıfın dışındaki kodun hangi verilere erişebileceği kontrol edilir.
  3. Inheritance (Kalıtım):
    • Kalıtım, bir sınıfın başka bir sınıftan özelliklerini ve davranışlarını miras almasıdır. Bu, kodun tekrar kullanılmasını sağlar ve kodun daha organize olmasını sağlar.
    • C# ve diğer OOP dillerinde, bir sınıf yalnızca bir sınıftan kalıtım alabilir (tekil kalıtım). Ancak, C# gibi dillerde birden fazla sınıfın özelliklerini miras almak için arayüzler (interfaces) kullanılabilir.
  4. Polymorphism (Çok Biçimlilik):
    • Polimorfizm, aynı adı taşıyan ancak farklı davranışlar gösteren metodların kullanılmasıdır. Bu, farklı sınıfların aynı adı taşıyan bir metodu farklı şekillerde uygulayabilmesini sağlar.
    • Polimorfizm, kalıtım ve arayüzler kullanılarak gerçekleştirilebilir. Bir sınıf, başka bir sınıfın metotlarını ezerek (override) veya bir arayüzü uygulayarak polimorfik davranış sergileyebilir.

C#, Java, C++, Python gibi diller, nesne yönelimli programlamanın temel kavramlarını destekler ve bu kavramları kullanarak karmaşık yazılım sistemleri geliştirmeyi sağlar. Bu nedenle, nesne yönelimli programlamayı anlamak ve kullanmak, modern yazılım geliştirme süreçlerinde temel bir beceridir. Bu kavramları anlayarak, yazılım projelerinizde daha modüler, esnek ve sürdürülebilir kodlar yazabilirsiniz.


Tasarım Şablonları İçin OOP Dışında Neler Var?

“Nesne Yönelimli Programlama” (OOP), yazılım geliştirme sürecinde kullanılan bir programlama paradigmasıdır ve “Tasarım Desenleri” ile OOP arasında bazı ayrımlar bulunmaktadır.

  1. OOP, sadece bir dilde değil, bir kavramlar setidir:
    • OOP, bir programlama dili olmanın ötesinde bir düşünme ve tasarlama yaklaşımıdır. Ancak tasarım desenleri, OOP’nin sunduğu kavramları kullanarak belirli problemlere yönelik çözüm yolları sunar. Dolayısıyla tasarım desenleri, sadece bir dilde değil, farklı OOP dillerinde de uygulanabilir.
  2. Tasarım desenleri, spesifik problemleri çözmek için geliştirilmiştir:
    • Tasarım desenleri, genellikle sıkça karşılaşılan problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir. OOP ise daha geniş bir programlama paradigmasıdır ve kod organizasyonu, veri saklama, modülerlik gibi genel prensipleri kapsar.
  3. Tasarım desenleri, programlama dillerinden bağımsızdır:
    • Tasarım desenleri, belirli bir programlama diline bağlı olmaksızın genel prensipler sunar. Bu nedenle, aynı tasarım deseni farklı programlama dillerinde farklı sözdizimiyle uygulanabilir.
  4. OOP, daha geniş bir kavramdır ve tasarım desenlerinin yanı sıra diğer programlama paradigmalarını da kapsar:
    • OOP, yalnızca kalıtım, kapsülleme ve polimorfizm gibi kavramlarla sınırlı değildir. OOP, aynı zamanda modülerlik, veri yapıları, algoritmalar gibi diğer programlama paradigmalarını da içerebilir. Tasarım desenleri, OOP’nin sadece bir yönünü temsil eder.
  5. OOP, tasarım desenleri olmadan da kullanılabilir:
    • OOP prensipleri, tasarım desenlerine dayanmaz. OOP’nin temel prensipleri, kodun daha organize, esnek ve sürdürülebilir olmasını sağlar. Ancak tasarım desenleri, belirli problemlere yönelik daha spesifik çözümleri ifade eder.

Sonuç olarak, OOP ve tasarım desenleri arasında bazı ayrımlar bulunmaktadır. OOP, geniş bir programlama paradigmasıdır ve tasarım desenleri sadece bu paradigmanın bir parçasıdır. Tasarım desenleri, belirli problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir ve OOP’nin sadece bir yönünü temsil ederler.

C#, Nesne Yönelimli Programlama ve Tasarım Şablonları

  1. C# (C Sharp):
    • C#, Microsoft tarafından geliştirilmiş, nesne yönelimli, genel amaçlı bir programlama dilidir.
    • C#, .NET Framework, .NET Core ve Mono gibi platformlarda geniş bir kullanım alanına sahiptir.
    • C#, Java ve C++ gibi diğer nesne yönelimli dillerle benzer özelliklere sahiptir ve OOP prensiplerini destekler.
  2. Nesne Yönelimli Programlama (OOP):
    • Nesne yönelimli programlama, yazılım geliştirme sürecinde kullanılan bir programlama paradigmasıdır.
    • OOP, verileri ve işlevleri birlikte gruplayarak, gerçek dünya nesnelerini modellemeye odaklanır.
    • OOP’nin temel prensipleri arasında kalıtım, kapsülleme, ve polimorfizm bulunur.
  3. Tasarım Desenleri (Design Patterns):
    • Tasarım desenleri, yazılım geliştirme sürecinde karşılaşılan tekrar eden problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir.
    • Bu desenler, yazılımın esnekliğini, sürdürülebilirliğini ve yeniden kullanılabilirliğini artırmayı hedefler.
    • Creational, Structural ve Behavioral olmak üzere üç ana kategori altında incelenirler.
  4. C#, OOP ve Tasarım Desenleri Arasındaki İlişki:
    • C#, nesne yönelimli bir dil olduğu için, OOP prensiplerini doğrudan destekler.
    • C#, OOP prensiplerini kullanarak tasarım desenlerini uygulamak için zengin bir yapıya sahiptir.
    • Örneğin, C#’da Singleton, Factory Method, Observer gibi yaygın tasarım desenleri doğrudan uygulanabilir ve dilin özelliklerinden faydalanarak daha etkili bir şekilde kullanılabilir.
  5. Tasarım Desenlerinin C#’da Kullanımı:
    • C#, tasarım desenlerini uygulamak için kapsamlı bir araç seti sunar.
    • C#’ın nesne yönelimli özellikleri, tasarım desenlerinin uygulanmasını kolaylaştırır.
    • .NET Framework ve .NET Core gibi platformlar, hazır tasarım desenlerini uygulamak için geniş bir kütüphane sağlar.

C#, Nesne Yönelimli Programlama ve Tasarım Desenleri, yazılım geliştirme sürecinde birlikte kullanılarak daha esnek, sürdürülebilir ve yeniden kullanılabilir yazılım sistemlerinin geliştirilmesine olanak sağlar. Bu nedenle, bu konuların derinlemesine anlaşılması ve etkin bir şekilde uygulanması, başarılı bir yazılım geliştirme pratiği için önemlidir.

Tasarım Şablonları Nedir?

Tasarım desenleri (design patterns), yazılım geliştirme sürecinde tekrar eden problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir. Bu desenler, deneyimli yazılım geliştiricileri tarafından yıllar boyunca geliştirilmiş ve belirli sorunları çözmek için etkili olduğu kanıtlanmıştır.

  1. Problemleri Çözmek İçin Genel Yaklaşımlar:
    • Tasarım desenleri, yazılım geliştirme sürecinde sıkça karşılaşılan problemleri çözmek için geliştirilmiş genel yaklaşımları ifade eder.
    • Bu desenler, deneyimli geliştiricilerin tecrübelerine dayanarak ortaya çıkar ve belirli bir problemi çözmek için test edilmiş ve kanıtlanmış çözümleri içerir.
  2. Esneklik ve Yeniden Kullanılabilirlik:
    • Tasarım desenleri, yazılımın esnekliğini ve yeniden kullanılabilirliğini artırmayı hedefler.
    • Belirli bir deseni uygulayan bir kod parçası, farklı problemleri çözmek için de kullanılabilir. Bu, yazılım geliştirme sürecinde tekrar eden kod yazma ihtiyacını azaltır.
  3. Çeşitli Kategoriler:
    • Tasarım desenleri, Creational (Yaratımsal), Structural (Yapısal) ve Behavioral (Davranışsal) olmak üzere genellikle üç ana kategori altında incelenir.
    • Creational desenler, nesnelerin nasıl oluşturulacağına ve birleştirileceğine odaklanır. Structural desenler, sınıfların ve nesnelerin nasıl bir araya getirileceğine ve yapılandırılacağına odaklanır. Behavioral desenler, nesnelerin nasıl birlikte çalışacağına ve bilgi alışverişi yapacağına odaklanır.
  4. Nesne Yönelimli Programlama (OOP) İle İlişki:
    • Tasarım desenleri genellikle nesne yönelimli programlama (OOP) dilleriyle kullanılır.
    • OOP prensipleri olan kalıtım, kapsülleme ve polimorfizm gibi kavramlar, tasarım desenlerinin uygulanmasını kolaylaştırır ve tasarım desenlerinin etkin bir şekilde kullanılmasını sağlar.
  5. Gerçek Dünya Örnekleri:
    • Tasarım desenleri, gerçek dünya problemlerini çözmek için kullanılabilir ve yaygın olarak kullanılan çözüm örüntüleridir.
    • Örnekler arasında Singleton (Tekillik), Factory Method (Fabrika Metodu), Observer (Gözlemci) ve Decorator (Dekoratör) gibi desenler bulunur.

Tasarım desenleri, yazılım geliştirme sürecinde kodun daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlar. Bu nedenle, tasarım desenlerinin derinlemesine anlaşılması ve etkin bir şekilde uygulanması, yazılım geliştirme pratiğinin önemli bir parçasıdır.

Tasarım Şablonlarına Neden İhtiyacınız Var?


Tasarım desenlerinin neden gerektiği konusu, yazılım geliştirme sürecinde karşılaşılan tekrar eden problemleri çözmek ve yazılımın daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlamakla ilgilidir.

  1. Tekrar Eden Problemleri Çözmek:
    • Yazılım geliştirme sürecinde belirli problemler sıkça tekrar eder. Tasarım desenleri, bu tekrar eden problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir.
    • Örneğin, Singleton deseni, yalnızca bir nesnenin oluşturulmasını sağlar ve bu nesneye global bir erişim noktası sağlar. Bu, kaynak tüketimini azaltmak veya paylaşılan bir kaynağa tek bir erişim noktası sağlamak için kullanılabilir.
  2. Esneklik ve Değişime Uyum Sağlama:
    • Tasarım desenleri, yazılımın esnekliğini artırır. Bu desenler, gelecekteki değişikliklere daha iyi uyum sağlayacak şekilde tasarlanmıştır.
    • Örneğin, Strategy deseni, farklı algoritmaları bir arayüz üzerinden ayrı nesneler olarak tanımlar. Bu, yeni bir algoritma eklemek veya mevcut bir algoritmayı değiştirmek gerektiğinde, sadece ilgili strateji sınıfını değiştirmenizi sağlar.
  3. Kodun Daha Okunabilir ve Bakımı Daha Kolay Olması:
    • Tasarım desenleri, kodun daha okunabilir ve bakımı daha kolay olmasını sağlar. Belirli bir deseni kullanan geliştiriciler, tasarım desenlerini anladıklarında kodu daha hızlı ve etkili bir şekilde okuyabilir, anlayabilir ve değiştirebilirler.
    • Örneğin, Observer deseni, bir nesnenin durumunda değişiklik olduğunda bağımlı nesnelerin otomatik olarak güncellenmesini sağlar. Bu, kodun daha modüler ve bakımı daha kolay olmasını sağlar.
  4. Yazılımın Yeniden Kullanılabilir Olması:
    • Tasarım desenleri, yazılımın yeniden kullanılabilirliğini artırır. Belirli bir deseni uygulayan bir kod parçası, farklı problemleri çözmek için de kullanılabilir. Bu, yazılım geliştirme sürecinde tekrar eden kod yazma ihtiyacını azaltır.
    • Örneğin, Factory Method deseni, nesnelerin nasıl oluşturulacağını genelleştirir ve alt sınıfların nesneleri oluşturma sürecini belirlemesine izin verir. Bu, kodun daha yeniden kullanılabilir olmasını sağlar.

Tasarım desenleri, yazılım geliştirme sürecinde tekrar eden problemleri çözmek, kodun daha esnek ve sürdürülebilir olmasını sağlamak, yazılımın daha okunabilir ve bakımı daha kolay olmasını sağlamak ve yazılımın yeniden kullanılabilirliğini artırmak için önemlidir. Bu nedenle, tasarım desenlerinin etkili bir şekilde kullanılması, başarılı bir yazılım geliştirme pratiğinin temel bir parçasıdır.

Tasarım Şablonlarının Kısa Tarihi

  1. Christopher Alexander ve Mimarlık Alanındaki Etkisi:
    • Tasarım desenlerinin kökeni, mimarlık alanında Christopher Alexander ve ekibinin çalışmalarına dayanır. Alexander, “The Timeless Way of Building” (Zamanın Ötesindeki Yapım) ve “A Pattern Language” (Bir Desen Dili) gibi eserlerinde, mimari projelerde kullanılan tekrar eden desenlerin gücünü vurgulamıştır.
    • Bu desenler, mimari yapıların tasarımında kullanılan temel kavramlar ve çözüm önerilerini ifade eder.
  2. Erich Gamma, Richard Helm, Ralph Johnson ve John Vlissides (Gang of Four – GoF):
    • Tasarım desenlerinin yazılım mühendisliği alanındaki popülerliği, 1994 yılında Erich Gamma, Richard Helm, Ralph Johnson ve John Vlissides’in “Design Patterns: Elements of Reusable Object-Oriented Software” (Tasarım Desenleri: Yeniden Kullanılabilir Nesne Yönelimli Yazılımın Unsurları) kitabının yayınlanmasıyla arttı.
    • Bu kitap, tasarım desenlerinin yazılım mühendisliği alanında uygulanmasına yönelik kapsamlı bir rehber sunar. Bu dört yazar, “Gang of Four” (GoF) olarak bilinir ve tasarım desenlerinin popülerleşmesinde önemli bir rol oynamışlardır.
  3. Yazılım Geliştirme Sürecindeki Yaygın Kullanımı:
    • Tasarım desenleri, yazılım geliştirme sürecinde tekrar eden problemleri çözmek için geliştirilmiş genel çözüm örüntüleridir. GoF kitabındaki desenler, yazılım geliştiricilerine belirli problemleri çözmek için kanıtlanmış çözümler sunar.
    • Günümüzde, tasarım desenleri, yazılım geliştirme sürecinin ayrılmaz bir parçasıdır ve neredeyse tüm yazılım projelerinde yaygın olarak kullanılır.
  4. Yeni Nesil Desenler ve Uygulamalar:
    • GoF kitabından sonra, birçok yeni tasarım deseni tanıtılmıştır ve yazılım geliştirme topluluğu tarafından benimsenmiştir. Bu desenler, özellikle yeni teknolojiler ve platformlar için optimize edilmiş çözümler sunar.
    • Ayrıca, çeşitli endüstrilerdeki farklı ihtiyaçları karşılamak için özelleştirilmiş tasarım desenleri de geliştirilmektedir.

Tasarım desenleri, yazılım geliştirme sürecinde tekrar eden problemleri çözmek ve yazılımın daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlamak için önemli bir rol oynamaktadır. Christopher Alexander’ın mimarlık alanındaki çalışmalarından başlayarak, GoF kitabının yayınlanmasına ve günümüzdeki geniş kabulüne kadar uzanan bir tarihsel evrimi temsil ederler.

Tasarım Şablonlarının Kategorileri

Tasarım desenleri genellikle üç ana kategori altında incelenir: Creational (Yaratımsal), Structural (Yapısal) ve Behavioral (Davranışsal). Her kategori, farklı türde problemleri çözmek için tasarlanmış desenleri içerir.

  1. Creational Patterns (Yaratımsal Desenler):
    • Yaratımsal desenler, nesnelerin nasıl oluşturulacağına ve birleştirileceğine odaklanır.
    • Bu desenler, nesne oluşturma sürecini soyutlar ve programın esnekliğini artırır. Nesne oluşturmayla ilgili karmaşık işlemleri gizler ve kodun yeniden kullanılabilirliğini artırır.
    • Örnekler arasında Singleton, Factory Method, Abstract Factory, Builder ve Prototype gibi desenler bulunur.
  2. Structural Patterns (Yapısal Desenler):
    • Yapısal desenler, sınıfların ve nesnelerin nasıl bir araya getirileceğine ve yapılandırılacağına odaklanır.
    • Bu desenler, sınıf ve nesne yapısını organize etmeye yardımcı olur ve farklı bileşenler arasındaki ilişkileri tanımlar.
    • Örnekler arasında Adapter, Bridge, Composite, Decorator, Facade ve Proxy gibi desenler bulunur.
  3. Behavioral Patterns (Davranışsal Desenler):
    • Davranışsal desenler, nesnelerin nasıl birlikte çalışacağına ve bilgi alışverişi yapacağına odaklanır.
    • Bu desenler, nesneler arasındaki iletişimi kolaylaştırır ve davranışlarını daha esnek hale getirir.
    • Örnekler arasında Observer, Strategy, Command, Iterator, State ve Visitor gibi desenler bulunur.

Her kategori, belirli bir türde problemleri çözmek için tasarlanmış desenleri içerir. Örneğin, Creational desenler, nesnelerin oluşturulması ve birleştirilmesiyle ilgili sorunları çözmek için kullanılırken, Structural desenler, sınıf ve nesne yapısını düzenlemek için kullanılır. Behavioral desenler ise nesneler arasındaki iletişim ve davranışları yönetmek için kullanılır.

Tasarım desenlerinin bu kategorilere ayrılması, geliştiricilere belirli türdeki problemlere yönelik çözüm önerilerini daha kolay tanımlama ve uygulama imkanı sunar. Bu desenlerin bilinmesi, yazılım geliştirme sürecinde daha etkili ve modüler kod yazma yeteneğini artırır ve genellikle yazılımın daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlar.

Tasarım Şablonlarıyla Başlamanın Yolları

Yazılım Tasarım Şablonlarını Kullanma

Yazılım tasarım desenlerini kullanmak, yazılım geliştirme sürecinde tekrar eden problemleri çözmek ve daha esnek, sürdürülebilir ve yeniden kullanılabilir kodlar oluşturmak için önemlidir.

  1. Tekrar Eden Problemleri Tanımlama:
    • Yazılım geliştirme sürecinde belirli problemler sıkça tekrar eder. Bu problemleri tanımlamak, uygun bir tasarım deseni seçmek için önemlidir.
    • Örneğin, bir nesnenin yalnızca bir kez oluşturulması gereken durumlar veya bir nesnenin farklı türlerde oluşturulması gereken durumlar gibi problemler tekrar edebilir.
  2. Uygun Tasarım Desenini Seçme:
    • Belirlenen probleme uygun bir tasarım deseni seçmek önemlidir. Creational, Structural ve Behavioral olmak üzere farklı kategorilerde birçok tasarım deseni bulunur.
    • Örneğin, Singleton deseni, yalnızca bir nesnenin oluşturulmasını sağlamak için kullanılabilirken, Decorator deseni bir nesnenin davranışını dinamik olarak genişletmek için kullanılabilir.
  3. Deseni Uygulama:
    • Seçilen tasarım desenini uygulamak için, desenin kurallarını ve önerilerini takip etmek gerekir. Bu genellikle belirli sınıfları ve arayüzleri tanımlamayı, bu sınıfları birleştirmeyi veya özel davranışları uygulamayı içerir.
    • Uygulamada, tasarım desenlerinin dilin özelliklerini ve kütüphanelerini kullanarak daha etkili ve optimize edilmiş çözümler oluşturmak önemlidir.
  4. Test Etme ve Ayarlama:
    • Tasarım desenlerini uyguladıktan sonra, oluşturulan çözümü test etmek ve gerekirse ayarlamak önemlidir. Bu, kodun doğru çalışmasını sağlamak ve performansı artırmak için gereklidir.
    • Test süreci, tasarım desenlerinin uygulanmasının doğru olduğunu doğrulamak ve olası hataları belirlemek için kritik bir adımdır.
  5. Belgeleme ve Paylaşma:
    • Tasarım desenlerini kullanırken, kodunuzu belgelemek ve diğer geliştiricilerle paylaşmak önemlidir. Bu, kodun daha anlaşılabilir ve sürdürülebilir olmasını sağlar.
    • Ayrıca, tasarım desenlerini kullanırken, projenizin tasarım kararlarını ve tasarım desenlerini belgelemek önemlidir. Bu, projenin daha iyi anlaşılmasını ve gelecekteki geliştirmeler için referans oluşturulmasını sağlar.

Yazılım tasarım desenlerini kullanmak, yazılım geliştirme sürecinde tekrar eden problemleri çözmek ve daha esnek, sürdürülebilir ve yeniden kullanılabilir kodlar oluşturmak için güçlü bir araçtır. Bu nedenle, tasarım desenlerinin kullanılması yazılım geliştirme sürecinin önemli bir parçasıdır ve geliştiricilerin bu konuda derin bir anlayışa sahip olmaları önemlidir.

Yazılım Tasarım Şablonlarının Dezavantajları

ılım tasarım desenlerinin kullanımı birçok avantaj sağlasa da, bazı dezavantajları da bulunmaktadır. Bu dezavantajlar, tasarım desenlerinin yanlış kullanımından, aşırı kullanımından veya belirli durumlarda uygun olmamasından kaynaklanabilir.

  1. Karmaşıklık Artışı:
    • Bazı tasarım desenleri, uygulama kodunu daha karmaşık hale getirebilir. Özellikle, desenin gerektirdiği ek sınıflar ve arayüzlerin eklenmesi, kodun daha fazla karmaşıklaşmasına neden olabilir.
    • Aşırı mühendislik, gereksiz desenlerin uygulanması veya desenlerin gereğinden fazla kullanılması, kodun daha karmaşık ve anlaşılması zor olmasına yol açabilir.
  2. Artan İşgücü ve Bakım Maliyeti:
    • Tasarım desenlerinin uygulanması, başlangıçta daha fazla iş gücü ve zaman gerektirebilir. Özellikle, desenlerin doğru bir şekilde uygulanması ve kodun tasarımına entegre edilmesi zaman alabilir.
    • Ayrıca, tasarım desenlerinin bakımı da önemlidir. Eğer bir tasarım deseni değiştirilmek veya güncellenmek zorunda kalırsa, bu değişikliklerin diğer bileşenlerle uyumlu olması gerekebilir. Bu da ek işgücü ve bakım maliyeti anlamına gelebilir.
  3. Over-Engineering Riski:
    • Bazı durumlarda, geliştiriciler tasarım desenlerini aşırı uygulayabilirler. Bu, gereksiz karmaşıklığa ve kodun gereksiz hale gelmesine neden olabilir.
    • Özellikle, küçük ölçekli projeler veya basit problemler için tasarım desenlerinin aşırı kullanılması, gereksiz mühendislikle sonuçlanabilir.
  4. Anlaşılabilirlik Zorlukları:
    • Tasarım desenlerini bilmeyen veya anlamayan geliştiriciler için, desenlerin uygulanması ve kodun anlaşılması zor olabilir. Bu, yeni geliştiricilerin projeye katılmasını veya kodun bakımını zorlaştırabilir.
    • Ayrıca, desenlerin doğru bir şekilde belgelenmemesi veya açıklanmaması durumunda, diğer geliştiricilerin kodu anlaması ve değiştirmesi daha da zorlaşabilir.
  5. Performans Etkisi:
    • Bazı tasarım desenleri, uygulamanın performansını olumsuz etkileyebilir. Özellikle, bazı desenlerin gerektirdiği ek katmanlar veya işlemler, uygulamanın çalışma zamanında daha fazla kaynak tüketebilir.
    • Bu nedenle, performans kritik uygulamalarda tasarım desenlerinin dikkatlice seçilmesi ve uygulanması önemlidir.

Yazılım tasarım desenlerinin bu dezavantajlarına rağmen, doğru şekilde kullanıldığında ve uygun bağlamlarda uygulandığında, kodun daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlayabilirler. Bu nedenle, geliştiricilerin tasarım desenlerini dikkatli bir şekilde kullanması ve her durumda uygun deseni seçmesi önemlidir.


C# için Yaratımsal Tasarım Şablonlarının Genel Bir Bakışı

Creational patterns (Yaratımsal desenler), nesnelerin nasıl oluşturulacağı ve birleştirileceğiyle ilgili problemleri çözmek için tasarlanmıştır. Bu desenler, nesne oluşturma sürecini soyutlar ve kodun daha esnek ve yeniden kullanılabilir olmasını sağlar.

  1. Singleton Pattern:
    • Singleton deseni, bir sınıfın yalnızca bir örneğinin oluşturulmasını sağlar ve bu örneğe genellikle global erişim sağlar.
    • Bu desen, bir nesnenin tek bir kopyasına ihtiyaç duyulduğunda ve bu nesnenin tüm uygulama boyunca paylaşılması gerektiğinde kullanışlıdır.
    • Singleton desenini uygulamak için, sınıfın bir örneğini statik olarak saklayan bir özellik ve bu örneği döndüren bir metodun olması gerekir.
    • Thread-safe bir singleton elde etmek için Lazy<T> sınıfı veya lock anahtar sözcüğü kullanılabilir.
  2. Factory Method Pattern:
    • Factory Method deseni, alt sınıfların nesneleri oluşturmasını belirleyen bir metod tanımlar. Böylece, bir üst sınıf, alt sınıflarının hangi nesneyi oluşturacağını kontrol edebilir.
    • Bu desen, nesne oluşturma sürecini soyutlar ve alt sınıfların bu süreci değiştirmesine olanak tanır.
    • C# dilinde, Factory Method deseni genellikle bir arayüz veya soyut bir sınıf kullanılarak uygulanır.
  3. Abstract Factory Pattern:
    • Abstract Factory deseni, bir grup ilgili nesnenin oluşturulmasını soyutlar ve bu nesnelerin birbiriyle uyumlu olmasını sağlar.
    • Bu desen, farklı nesnelerin bir arada kullanılması gerektiğinde veya bir ailenin birden fazla varyasyonu arasında geçiş yapılması gerektiğinde kullanışlıdır.
    • Abstract Factory deseni, soyut fabrika sınıfı ve bu sınıftan türetilen özelleştirilmiş fabrika sınıfları kullanılarak uygulanır.
  4. Builder Pattern:
    • Builder deseni, karmaşık nesnelerin adım adım oluşturulmasını sağlar. Bu desen, nesne oluşturma sürecini adım adım soyutlar ve nesne oluşturma adımlarını ayrı ayrı yönetilir hale getirir.
    • Bu desen, nesnelerin birbiri ardına yapılandırılması gerektiğinde veya nesnelerin karmaşık bir şekilde oluşturulması gerektiğinde kullanışlıdır.
    • Builder deseni, oluşturulacak nesne tipine özgü bir yapıya sahip bir Builder sınıfı ve bu sınıfı kullanan bir Director sınıfı kullanılarak uygulanır.

Bu creational patternler, C# dilinde nesnelerin oluşturulması ve yapılandırılmasıyla ilgili çeşitli problemleri çözmek için kullanılabilir. Her biri farklı senaryolar için tasarlanmıştır ve uygun şekilde kullanıldığında, kodun daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlar.

C# için Yapısal Tasarım Şablonlarının Genel Bir Bakışı


Structural patterns (Yapısal desenler), yazılım bileşenlerinin nasıl bir araya getirileceği ve yapılandırılacağıyla ilgili problemleri çözmek için tasarlanmıştır. Bu desenler, sınıf ve nesneler arasındaki ilişkileri düzenler ve bileşenler arasındaki etkileşimi kolaylaştırır. C# dilinde birçok yapısal desen bulunmaktadır.

  1. Adapter Pattern:
    • Adapter deseni, mevcut bir sınıfın arayüzünü, başka bir sınıfın beklediği arayüze dönüştürmek için kullanılır.
    • Bu desen, farklı arayüzleri veya sınıf yapılarını birbiriyle uyumlu hale getirmek için kullanışlıdır.
    • C# dilinde, Adapter deseni genellikle sınıf adaptörü veya nesne adaptörü şeklinde uygulanır.
  2. Bridge Pattern:
    • Bridge deseni, soyutlamayı gerçek uygulamadan ayırarak, soyut sınıfı ve onun gerçek uygulamasını ayrı ayrı genişletir.
    • Bu desen, sınıf hiyerarşisini ve uygulamayı birbirinden bağımsız hale getirir, böylece her ikisinin de değiştirilmesi ve genişletilmesi daha kolay olur.
    • C# dilinde, Bridge deseni genellikle arayüzler ve soyut sınıflar kullanılarak uygulanır.
  3. Composite Pattern:
    • Composite deseni, ağaç yapıları gibi hiyerarşik nesneleri temsil etmek için kullanılır. Bu desen, tekil nesneleri ve bileşik nesneleri aynı arayüzde birleştirir.
    • Bu desen, bir nesneyi veya bir grup nesneyi tek bir nesne gibi ele almak istediğinizde veya nesneleri hiyerarşik bir yapıda düzenlemek istediğinizde kullanışlıdır.
    • C# dilinde, Composite deseni genellikle bir arayüz veya soyut sınıf kullanılarak uygulanır ve genellikle ağaç yapıları oluşturmak için kullanılır.
  4. Decorator Pattern:
    • Decorator deseni, bir nesneyi dinamik olarak genişletmek veya değiştirmek için kullanılır. Bu desen, nesnenin davranışını çalışma zamanında değiştirmek için kullanılır.
    • Bu desen, nesnelerin farklı özelliklerini bir arada kullanmak istediğinizde veya nesnelerin davranışlarını değiştirmek istediğinizde kullanışlıdır.
    • C# dilinde, Decorator deseni genellikle bir soyut sınıf veya arayüz kullanılarak uygulanır ve genellikle miras alma yerine kompozisyon kullanır.
  5. Facade Pattern:
    • Facade deseni, karmaşık bir alt sistem veya kütüphanenin önünde basit bir arayüz sağlar. Bu desen, alt sistemlerin karmaşıklığını gizler ve kullanımını kolaylaştırır.
    • Bu desen, bir alt sistemi kullanırken karmaşık yapıları ve işlemleri gizlemek istediğinizde veya bir kütüphaneyi daha kullanılabilir hale getirmek istediğinizde kullanışlıdır.
    • C# dilinde, Facade deseni genellikle bir sınıf veya arayüz kullanılarak uygulanır ve genellikle bir ‘Facade’ adında bir sınıf olarak adlandırılır.

Bu yapısal desenler, C# dilinde sınıf ve nesnelerin bir araya getirilmesi ve yapılandırılmasıyla ilgili çeşitli problemleri çözmek için kullanılabilir. Her biri farklı senaryolar için tasarlanmıştır ve uygun şekilde kullanıldığında, kodun daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlar.

C# için Davranışsal Tasarım Şablonlarının Genel Bir Bakışı


Behavioral patterns (Davranışsal desenler), nesneler arasındaki davranışları ve iletişimi düzenlemek için tasarlanmıştır. Bu desenler, nesnelerin birbirleriyle nasıl etkileşimde bulunacaklarını ve davranacaklarını belirler.

  1. Observer Pattern:
    • Observer deseni, bir nesnenin durumu değiştiğinde bağımlı olan ve bu değişikliklerden etkilenen diğer nesneleri otomatik olarak güncellemek için kullanılır.
    • Bu desen, birçok nesnenin birbirleriyle bağlantılı olduğu durumlarda veya bir nesnenin durumu değiştiğinde birçok nesnenin buna tepki vermesi gerektiği durumlarda kullanışlıdır.
    • C# dilinde, Observer deseni genellikle bir arayüz kullanılarak uygulanır ve genellikle .NET Framework’te bulunan olaylarla entegre edilir.
  2. Strategy Pattern:
    • Strategy deseni, bir algoritmanın farklı varyasyonlarını tanımlar ve bu algoritmaları değiştirilebilir hale getirir. Bu desen, bir algoritmanın çalışma zamanında değiştirilmesi gerektiğinde kullanışlıdır.
    • Bu desen, bir algoritmanın birçok farklı varyasyonunu uygulamak ve algoritmalar arasında geçiş yapmak istediğinizde kullanılır.
    • C# dilinde, Strategy deseni genellikle bir arayüz veya soyut bir sınıf kullanılarak uygulanır ve farklı algoritmaları temsil eden sınıflar tarafından uygulanır.
  3. Command Pattern:
    • Command deseni, bir isteği nesne olarak temsil eder ve bu isteği bir alıcıya iletmek için kullanılır. Bu desen, bir isteği farklı nesneler arasında iletmek ve isteği sıraya almak için kullanışlıdır.
    • Bu desen, bir isteği parametrik bir nesne olarak temsil etmek ve isteği farklı nesneler arasında geçirmek istediğinizde kullanılır.
    • C# dilinde, Command deseni genellikle bir arayüz veya soyut bir sınıf kullanılarak uygulanır ve bir isteği temsil eden sınıflar tarafından uygulanır.
  4. Iterator Pattern:
    • Iterator deseni, bir koleksiyonun elemanlarına sırayla erişmek için bir arabirim sağlar. Bu desen, bir koleksiyonun yapısını gizler ve koleksiyonun elemanlarına erişimi standartlaştırır.
    • Bu desen, bir koleksiyonun elemanları üzerinde döngü yapıp her bir elemana ayrı ayrı erişmek istediğinizde kullanışlıdır.
    • C# dilinde, Iterator deseni genellikle IEnumerable ve IEnumerator arabirimleri kullanılarak uygulanır ve .NET Framework’te bulunan koleksiyonlarla entegre edilir.
  5. State Pattern:
    • State deseni, bir nesnenin durumunu temsil eden bir dizi sınıfı tanımlar ve nesnenin durumunu değiştirmek için bir arayüz sağlar. Bu desen, bir nesnenin davranışının durumuna bağlı olarak değiştiği durumlarda kullanışlıdır.
    • Bu desen, bir nesnenin farklı durumlarını temsil etmek ve her durum için farklı davranışlar sağlamak istediğinizde kullanılır.
    • C# dilinde, State deseni genellikle bir arayüz veya soyut bir sınıf kullanılarak uygulanır ve farklı durumları temsil eden sınıflar tarafından uygulanır.

Bu davranışsal desenler, C# dilinde nesneler arasındaki iletişimi düzenlemek ve değişen durumlara uyum sağlamak için kullanılabilir. Her biri farklı senaryolar için tasarlanmıştır ve uygun şekilde kullanıldığında, kodun daha esnek, sürdürülebilir ve yeniden kullanılabilir olmasını sağlar.

The Iterator Pattern

Iterator Pattern Tanımı

Iterator deseni, bir koleksiyonun elemanlarına sırayla erişmek için bir arabirim sağlar ve koleksiyonun yapısını gizler. Bu desen, bir döngü kullanarak koleksiyonun her bir elemanına erişmeyi ve bu elemanlar üzerinde işlem yapmayı sağlar. Iterator deseni, koleksiyonun yapısını değiştirmeden veya koleksiyonun iç yapısını müşterilere açıklamadan koleksiyon üzerinde döngü yapmayı kolaylaştırır.

Iterator deseni genellikle “Iterator” ve “Aggregate” olmak üzere iki ana bileşenden oluşur:

  1. Iterator (Yineleyici): Iterator, koleksiyonun elemanlarına sırayla erişmek için bir arayüz veya soyut bir sınıfı temsil eder. Bu arayüz, koleksiyonun elemanlarına erişim için gerekli metodları içerir. Iterator, koleksiyonun yapısını gizler ve koleksiyonun iç yapısını müşterilere açıklamaz. Genellikle “MoveNext()”, “Reset()”, “CurrentItem” gibi metodları içerir.
  2. Aggregate (Toplayıcı): Aggregate, bir koleksiyonun yapısını temsil eder ve koleksiyon üzerinde döngü yapılabilmesi için bir iterator döndürür. Aggregate, müşteriye bir iterator sağlar ve bu iterator üzerinden koleksiyonun elemanlarına erişim sağlar. Aggregate, genellikle bir arayüz veya soyut bir sınıf olarak tanımlanır ve koleksiyonun yapısını müşterilere açıklamaz.

Iterator deseni, bir dizi eleman içeren koleksiyonlarda kullanışlıdır ve koleksiyonun elemanlarına erişimi standartlaştırır. Bu desen, koleksiyonun yapısını gizleyerek koleksiyon üzerinde değişiklikler yapılmasını ve koleksiyonun iç yapısının müşterilere açıklanmasını önler. Bu da kodun daha modüler ve sürdürülebilir olmasını sağlar.

C# dilinde, Iterator deseni genellikle “IEnumerable” ve “IEnumerator” arabirimleri kullanılarak uygulanır. IEnumerable arabirimi, koleksiyon üzerinde döngü yapılabilmesi için GetEnumerator() metodunu sağlar ve IEnumerator arabirimi, koleksiyonun elemanlarına erişim için MoveNext(), Reset() ve Current metodlarını sağlar. Bu arabirimler, .NET Framework’te bulunan koleksiyonların çoğunda kullanılır ve Iterator desenini uygulamak için kullanılabilirler.

Oluşturulan Veri İçin C# ‘de Açık Döngü İteratörü Uygulaması


C#’da üretilen veriler için açık yineleyici (explicit iterator) uygulaması, bir sınıfın öğelerine birbirini takip eden bir şekilde erişmek için bir mekanizma sağlar. Bu, özellikle büyük miktarlarda veri üreten veya dinamik olarak oluşturulan veri yapılarıyla çalışırken kullanışlıdır. Bu tür veri yapılarına örnek olarak, sonsuz bir dizi, belirli bir kurala göre üretilen veri koleksiyonları veya büyük veri tabanlarından alınan kayıtlar verilebilir.

using System;
using System.Collections;
using System.Collections.Generic;

// Örnek bir sınıf: Sonsuz bir sayı dizisi üreten bir sınıf
public class InfiniteNumbers : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        int number = 0;
        while (true)
        {
            yield return number++; // Sonsuz dizi elemanlarını üret
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator(); // Genel GetEnumerator metoduna yönlendirme
    }
}

class Program
{
    static void Main(string[] args)
    {
        InfiniteNumbers numbers = new InfiniteNumbers();

        // Açık yineleyici kullanımı
        foreach (var number in numbers)
        {
            Console.WriteLine(number);

            if (number >= 10) // Sadece ilk 10 sayıyı göster
                break;
        }
    }
}

Yukarıdaki örnekte, InfiniteNumbers adlı bir sınıf, sonsuz bir dizi oluşturan bir sınıfı temsil etmektedir. Bu sınıf, IEnumerable<int> arabirimini uygular, bu da bu sınıfın yineleyici kullanımını mümkün kılar. GetEnumerator() metodu, sonsuz bir dizi elemanını üretirken yield return ifadesini kullanarak bir sonraki elemanı döndürür.

InfiniteNumbers sınıfının bir örneği oluşturulduktan sonra, bu örneğin bir döngü içinde kullanılmasıdır. foreach döngüsü, numbers örneğinin her bir elemanına erişmek için açık yineleyiciyi kullanır. foreach döngüsü, belirtilen koşul gerçekleşene kadar çalışır ve her döngü adımında bir sonraki elemana geçer.

Bu örnekte, InfiniteNumbers sınıfı özel bir durum oluşturur ve açık yineleyici ile bu durumda başa çıkmak kolaydır. Ancak, farklı senaryolarda, belirli bir veri yapısının veya veri kaynağının el ile dolaşılması gerekebilir. Bu durumda, sınıf içinde bir yineleyici (iterator) yöntemi oluşturmak ve bu yöntemi çağırmak gerekebilir.

Bu tür özel veri yapılarını dolaşırken açık yineleyici kullanımı, performansı artırabilir, bellek kullanımını azaltabilir ve kodun daha temiz ve okunabilir olmasını sağlayabilir. Bu nedenle, büyük veri kümesi veya dinamik olarak oluşturulan veri yapıları ile çalışırken açık yineleyici uygulaması önemli bir teknik olabilir.

C#’de LINQ ile İteratörlerin Birleştirilmesi

C#’da LINQ (Language Integrated Query), veri koleksiyonları üzerinde sorgular oluşturmak ve işlem yapmak için güçlü bir araçtır. LINQ kullanarak, C# dilinde yineleyicileri (iterators) birleştirmek ve karmaşık sorgular oluşturmak oldukça kolaydır. Bu, veri koleksiyonları üzerinde filtreleme, sıralama, gruplama ve dönüşümler gibi çeşitli işlemleri gerçekleştirmenizi sağlar.

  1. LINQ ve IEnumerable/IEnumerator:
    • LINQ, IEnumerable ve IEnumerator arabirimlerini kullanan yineleyicilerle uyumludur. Bu nedenle, bir koleksiyonunuzun bu arabirimleri uygulaması durumunda, LINQ kullanarak bu koleksiyonları sorgulayabilirsiniz.
  2. LINQ Query Syntax ve Method Syntax:
    • LINQ, iki farklı sözdizimi sunar: sorgu sözdizimi (query syntax) ve yöntem sözdizimi (method syntax). Sorgu sözdizimi, SQL benzeri bir görünüm sunar ve genellikle daha okunabilir ve anlaşılır bir yapıya sahiptir. Yöntem sözdizimi ise işlemleri zincirleme yöntemiyle birbirine bağlar ve daha programatik bir yaklaşım sunar.
  3. Filtreleme ve Sıralama:
    • LINQ, Where ve OrderBy gibi yöntemlerle veri koleksiyonlarını filtrelemek ve sıralamak için kullanılabilir. Örneğin:
var filteredList = myList.Where(item => item.Property > 10).OrderBy(item => item.Property);

4.Gruplama ve Dönüşümler:

  • LINQ, GroupBy ve Select gibi yöntemlerle veri koleksiyonlarını gruplamak ve dönüştürmek için kullanılabilir. Örneğin:
var groupedData = myList.GroupBy(item => item.Category);
var transformedData = myList.Select(item => new { Name = item.Name, Value = item.Property * 2 });

5.Birleştirme ve Birleştirilmiş Sonuçlar:

  • LINQ, Join ve Concat gibi yöntemlerle farklı koleksiyonları birleştirmek ve birleştirilmiş sonuçlar elde etmek için kullanılabilir. Örneğin:
var joinedData = myList1.Join(myList2, item1 => item1.Key, item2 => item2.Key, (item1, item2) => new { Item1 = item1, Item2 = item2 });
var concatenatedList = myList1.Concat(myList2);
  1. LINQ ve Deferred Execution:
    • LINQ sorguları, gecikmeli (deferred) yürütme prensibine dayanır. Bu, bir LINQ sorgusunun yalnızca gerektiğinde yürütüleceği anlamına gelir. Bu, veri koleksiyonları üzerinde daha verimli ve etkili işlemler yapmanızı sağlar.

LINQ, C# dilinde yineleyicileri birleştirmek ve veri koleksiyonları üzerinde çeşitli işlemler gerçekleştirmek için güçlü bir araçtır. Bu sayede, karmaşık veri manipülasyonları ve sorguları kolaylıkla gerçekleştirebilirsiniz. LINQ’in bu esnekliği, kodunuzu daha okunabilir, temiz ve sürdürülebilir hale getirebilir.

Factory Method Pattern

Factory Method Deseni Tanımı

Factory Method tasarım deseni, bir süper sınıfın alt sınıflarının nesne oluşturma sürecini, alt sınıflara bırakması için bir çerçeve sağlar. Bu desen, nesne oluşturma sürecini genelleştirir ve alt sınıfların bu süreci özelleştirmesine olanak tanır. Bu sayede, bir süper sınıf, nesne oluşturma sürecinin detaylarından soyutlanır ve alt sınıflar, kendi ihtiyaçlarına göre nesneleri nasıl oluşturacaklarını belirleyebilirler.

Factory Method deseni genellikle şu bileşenlerden oluşur:

  1. Creator (Yaratıcı):
    • Creator, genellikle bir sınıf veya bir arayüz olarak tanımlanır ve alt sınıfların nesne oluşturma sürecini genelleştirir. Bu sınıf, üretilecek nesnelerin soyut bir tanımını sağlar ve fabrika yöntemini içerir.
  2. Concrete Creators (Somut Yaratıcılar):
    • Concrete Creators, Creator sınıfından türetilen ve fabrika yöntemini uygulayan alt sınıflardır. Her bir somut yaratıcı sınıfı, belirli bir türde nesneleri oluşturmak için gereken iş mantığını içerir.
  3. Product (Ürün):
    • Product, genellikle bir sınıf veya bir arayüz olarak tanımlanır ve oluşturulacak nesnelerin ortak bir arayüzünü sağlar. Concrete Creator sınıfları, Product arayüzünü uygulayan nesneleri oluşturur.
  4. Concrete Products (Somut Ürünler):
    • Concrete Products, Product arayüzünü uygulayan ve Concrete Creator sınıfları tarafından oluşturulan nesnelerdir. Her somut ürün, belirli bir türde nesnenin uygulama ayrıntılarını içerir.

Factory Method deseninde, Creator sınıfı, nesne oluşturma sürecinin genel yapısal taslağını belirlerken, Concrete Creator sınıfları, bu sürecin detaylarını uygular. Bu, yaratıcı sınıfın alt sınıflarının, hangi türde nesnelerin oluşturulacağını belirlemesine olanak tanır ve nesne oluşturma sürecini alt sınıflara bırakır.

Factory Method deseni, aşağıdaki durumlar için uygundur:

  • Nesnelerin oluşturulma sürecinde genelleştirme ve soyutlama sağlamak istendiğinde.
  • Bir süper sınıfın alt sınıflarının, oluşturulan nesnelerin türlerini belirlemesine izin vermek istendiğinde.
  • Nesnelerin oluşturulma sürecinin değiştirilmesine ve özelleştirilmesine olanak tanımak istendiğinde.

Factory Method deseni, birçok tasarım deseniyle birlikte kullanılabilir ve uygulama kodunun daha esnek, sürdürülebilir ve genişletilebilir olmasını sağlar.

Dinamik olarak Bir Eylemin Uygulamasını Seçmek İçin Factory Method Deseni


Factory Method deseni, bir süper sınıfın alt sınıflarının nesne oluşturma sürecini, alt sınıflara bırakması için bir çerçeve sağlar. Bu desen genellikle farklı alt sınıfların belirli bir işlevselliği farklı şekillerde uygulaması gerektiğinde kullanılır. Bir işlevselliğin hangi alt sınıf tarafından gerçekleştirileceğinin dinamik olarak belirlenmesi gerektiğinde Factory Method deseni ideal bir çözümdür.

Bu senaryoyu daha iyi anlamak için bir örnek düşünelim:

Örneğin, bir uygulama içinde farklı veritabanlarına bağlanma işlevselliği olsun. Uygulamanız MySQL, PostgreSQL ve SQLite gibi farklı veritabanlarına bağlanabilmelidir. Her bir veritabanı türü için farklı bağlantı nesneleri ve bağlantı işlevselliği gerekebilir. Ancak, uygulama seviyesinde hangi veritabanına bağlanılacağı çalışma zamanında belirlenebilir.

Bu senaryoda Factory Method deseni şu şekilde uygulanabilir:

Creator (Yaratıcı):

  • Yaratıcı sınıf, genellikle bir arayüz veya soyut bir sınıf olarak tanımlanır. Bu sınıf, farklı alt sınıfların ortak bir arayüzünü sağlar ve fabrika yöntemini tanımlar. Örneğin:
public abstract class DatabaseConnector
{
    public abstract Connection Connect();
}

Concrete Creators (Somut Yaratıcılar):

  • Somut yaratıcılar, Creator sınıfından türetilen ve fabrika yöntemini uygulayan alt sınıflardır. Her bir somut yaratıcı sınıfı, belirli bir veritabanı türü için bağlantı nesnelerini oluşturan işlemleri içerir. Örneğin:
public class MySqlConnectionCreator : DatabaseConnector
{
    public override Connection Connect()
    {
        return new MySqlConnection();
    }
}

public class PostgreSqlConnectionCreator : DatabaseConnector
{
    public override Connection Connect()
    {
        return new PostgreSqlConnection();
    }
}

Product (Ürün):

  • Ürün sınıfı, bağlantı nesnelerinin ortak bir arayüzünü sağlar. Örneğin:
public abstract class Connection
{
    public abstract void Open();
    public abstract void Close();
}

Concrete Products (Somut Ürünler):

  • Somut ürünler, Product arayüzünü uygulayan ve somut yaratıcı sınıflar tarafından oluşturulan nesnelerdir. Her somut ürün, belirli bir veritabanı türüne bağlanmak için gerekli işlevselliği sağlar. Örneğin:
public class MySqlConnection : Connection
{
    public override void Open()
    {
        // MySQL veritabanına bağlanma işlemleri
    }

    public override void Close()
    {
        // MySQL veritabanı bağlantısını kapatma işlemleri
    }
}

public class PostgreSqlConnection : Connection
{
    public override void Open()
    {
        // PostgreSQL veritabanına bağlanma işlemleri
    }

    public override void Close()
    {
        // PostgreSQL veritabanı bağlantısını kapatma işlemleri
    }
}

Bu yapı, uygulama seviyesinde dinamik olarak belirlenen bir veritabanı türüne göre doğru bağlantı nesnesini oluşturmak için kullanılabilir. Örneğin:

DatabaseConnector connector;

if (userChoosesMySQL)
{
    connector = new MySqlConnectionCreator();
}
else
{
    connector = new PostgreSqlConnectionCreator();
}

Connection connection = connector.Connect();
connection.Open();
// Veritabanına bağlanma işlemleri
connection.Close();

Bu şekilde, uygulama seviyesinde hangi veritabanı türünün kullanılacağı belirlenir ve buna göre doğru bağlantı nesnesi oluşturulur. Bu dinamik yapı, Factory Method deseninin gücünü gösterir ve kodun daha esnek, sürdürülebilir ve genişletilebilir olmasını sağlar.

Birim Testleri İçin Factory Method Tasarım Deseni

Factory Method tasarım deseni, bir süper sınıfın alt sınıflarının nesne oluşturma sürecini, alt sınıflara bırakması için bir çerçeve sağlar. Bu desen, nesne oluşturma sürecini genelleştirir ve alt sınıfların bu süreci özelleştirmesine olanak tanır. Bu sayede, bir süper sınıf, nesne oluşturma sürecinin detaylarından soyutlanır ve alt sınıflar, kendi ihtiyaçlarına göre nesneleri nasıl oluşturacaklarını belirleyebilirler.

Unit testing (birim test) ise yazılan kodun parçalarının, yazılım geliştirme sürecinde ayrı ayrı ve bağımsız olarak test edilmesi sürecidir. Factory Method deseni, birim testlerde de oldukça faydalıdır ve kodun test edilebilirliğini artırır.

  1. Dependency Injection (Bağımlılık Enjeksiyonu):
    • Factory Method deseni, bağımlılıkların enjekte edilmesini kolaylaştırır. Nesnelerin somut türlerine bağımlı olmaktansa, onları bir arayüz veya soyut bir sınıf üzerinden yaratmak, birim testlerin yapılmasını kolaylaştırır. Birim testlerde, sahte nesnelerin (mock objects) veya taklit nesnelerin (stub objects) kullanılması yaygındır. Factory Method deseni, bu taklit nesnelerinin oluşturulmasını kolaylaştırır.
  2. Sınıfların Değiştirilebilirliği:
    • Factory Method deseni, nesne oluşturma sürecini alt sınıflara bıraktığı için, farklı birim test senaryoları için farklı alt sınıflar oluşturmak kolaydır. Bu, kodun birim testler için daha esnek hale gelmesini sağlar. Örneğin, birim test senaryolarının her biri için farklı türde sahte nesneler oluşturmak gerekebilir. Factory Method deseni, bu farklı nesne türlerini kolayca oluşturmayı sağlar.
  3. Sürdürülebilirlik ve Okunabilirlik:
    • Factory Method deseni, kodun sürdürülebilirliğini artırır ve okunabilirliğini artırır. Nesne oluşturma sürecinin ayrı bir yöntemde tanımlanması, kodun daha modüler hale gelmesini sağlar. Bu, birim testlerin yazılmasını ve bakımını kolaylaştırır.
  4. Mock ve Stub Nesnelerin Oluşturulması:
    • Factory Method deseni, birim testlerde mock ve stub nesnelerin oluşturulmasını kolaylaştırır. Her alt sınıf, farklı bir mock veya stub nesnesini oluşturabilir ve bu nesneleri birim testlerde kullanabilir. Bu, birim testlerin daha etkili ve verimli olmasını sağlar.

Factory Method deseninin birim testlerle kullanımını daha iyi anlamak için basit bir örnek üzerinden ilerleyelim. Bu örnekte, bir matematik işlemi yapan bir sınıf oluşturacağız ve bu sınıfı test etmek için Factory Method desenini kullanacağız.

Öncelikle, matematik işlemi yapan bir arayüz oluşturalım:

public interface IOperation
{
    int Calculate(int a, int b);
}

Ardından, bu arayüzü uygulayan somut sınıflar oluşturalım:

public class AdditionOperation : IOperation
{
    public int Calculate(int a, int b)
    {
        return a + b;
    }
}

public class SubtractionOperation : IOperation
{
    public int Calculate(int a, int b)
    {
        return a - b;
    }
}

Şimdi, Factory Method desenini kullanarak işlem sınıflarını oluşturan bir Creator sınıfı oluşturalım:

public abstract class OperationFactory
{
    public abstract IOperation CreateOperation();
}

Bu Creator sınıfından türetilmiş somut yaratıcı sınıfları oluşturalım:

public class AdditionOperationFactory : OperationFactory
{
    public override IOperation CreateOperation()
    {
        return new AdditionOperation();
    }
}

public class SubtractionOperationFactory : OperationFactory
{
    public override IOperation CreateOperation()
    {
        return new SubtractionOperation();
    }
}

Şimdi, bu yapıyı birim testlerde kullanarak test edelim. İlk olarak, bir Factory Method ile birim testlerde mock nesneler oluşturan bir test sınıfı oluşturalım:

public class TestOperationFactory
{
    [Fact]
    public void TestAdditionOperation()
    {
        // Arrange
        OperationFactory factory = new AdditionOperationFactory();
        IOperation operation = factory.CreateOperation();

        // Act
        int result = operation.Calculate(3, 2);

        // Assert
        Assert.Equal(5, result);
    }

    [Fact]
    public void TestSubtractionOperation()
    {
        // Arrange
        OperationFactory factory = new SubtractionOperationFactory();
        IOperation operation = factory.CreateOperation();

        // Act
        int result = operation.Calculate(3, 2);

        // Assert
        Assert.Equal(1, result);
    }
}

Bu test sınıfı, Factory Method desenini kullanarak farklı işlem türlerini test etmek için mock nesneler oluşturur. Her bir test metodu, ilgili işlem türü için bir Factory sınıfı oluşturur ve bu fabrika yöntemini kullanarak işlem nesnesini alır. Sonra, bu işlem nesnesini kullanarak birim testlerini gerçekleştirir.

Bu örnek, Factory Method deseninin birim testlerde nasıl kullanılacağını göstermektedir. Bu desen, birim testlerin daha modüler ve esnek olmasını sağlar, çünkü işlem nesnelerinin oluşturulması alt sınıflara bırakılarak kod tekrarını önler ve birim testlerin kolayca değiştirilmesini sağlar.

The Adapter Pattern

Adapter Pattern Tanımı

Adapter Pattern, diğer bir deyişle Wrapper Pattern olarak da bilinir, farklı arayüzleri birbirine uyumlu hale getirmek için kullanılan bir yapısal tasarım desenidir. Bu desen, bir sınıfın arayüzünü, bir başka arayüze dönüştürmek için kullanılır. Böylece, bir sınıfın kullanımını, beklenen bir arayüzü kullanmak için uygundur.

Adapter Pattern’ın temel amacı, iki uyumsuz arayüz arasında köprü oluşturmaktır. Bu, yeni bir sınıf oluşturmadan var olan sınıfları uyumlu hale getirmeyi sağlar. Adapter Pattern’ın genellikle üç temel bileşeni vardır:

  1. Target (Hedef):
    • Target, istemcinin beklediği arayüzü temsil eder. İstemci, bu arayüzü kullanarak adaptöre erişir.
  2. Adaptee (Adapte Edilen):
    • Adaptee, adapte edilmek istenen sınıfı veya arayüzü temsil eder. Adaptee, istemcinin beklediği arayüzden farklı bir arayüze sahiptir.
  3. Adapter (Adaptör):
    • Adapter, Target arayüzünü uygulayan ve Adaptee arayüzünü kullanarak Target arayüzünü sağlayan sınıftır. Bu sayede, istemci, Adapter aracılığıyla Adaptee’nin işlevselliğine erişebilir.

Adapter Pattern’ın temel prensibi, adaptör sınıfının, istemcinin beklediği arayüzü uygulaması ve bu arayüzü kullanarak adaptörün Adaptee’ye yönlendirmesi veya dönüştürmesidir. Bu, kodun yeniden kullanılabilirliğini artırır ve mevcut kodu değiştirmeden yeni bir arayüzle uyumlu hale getirilmesini sağlar.

Adapter Pattern, aşağıdaki durumlar için uygun olabilir:

  • Bir üçüncü taraf kütüphanesi veya API’nin arayüzü, uygulamanızın beklediği arayüzle uyumlu değilse.
  • Mevcut bir sınıfın arayüzü değiştiğinde, mevcut kodun yeniden yazılmasını önlemek için.
  • Mevcut bir sınıfın veya modülün işlevselliğini genişletmek veya değiştirmek için.

Adapter Pattern, kodun daha modüler, esnek ve sürdürülebilir olmasını sağlar. Bu desen, farklı arayüzler arasında geçiş yapmanın gerektiği durumlarda çok kullanışlıdır ve yazılım projelerinde sıkça karşılaşılan bir sorunu çözmek için etkilidir.

Adapter in C#: FileStream’i ILogger’a Uyarlama

Adapter Pattern’ı C# ile FileStream sınıfını ILogger arayüzüyle uyumlu hale getirmek için kullanabiliriz. Bu senaryoda, FileStream sınıfının işlevselliğini ILogger arayüzüne dönüştüren bir adaptör sınıfı oluşturacağız.

Öncelikle, ILogger arayüzümüzü tanımlayalım:

public interface ILogger
{
    void Log(string message);
}

Ardından, adaptör sınıfımızı oluşturalım. Bu sınıf, FileStream’ın işlevselliğini ILogger arayüzüyle uyumlu hale getirecektir:

public class FileStreamAdapter : ILogger
{
    private readonly string _filePath;

    public FileStreamAdapter(string filePath)
    {
        _filePath = filePath;
    }

    public void Log(string message)
    {
        // FileStream'ı kullanarak dosyaya loglama işlemi yapılır
        using (var streamWriter = new StreamWriter(_filePath, true))
        {
            streamWriter.WriteLine(message);
        }
    }
}

Bu adaptör sınıfı, ILogger arayüzünü uygular ve içerisinde bir FileStream nesnesi oluşturur. Log metodu, aldığı mesajı FileStream ile dosyaya yazma işlemini gerçekleştirir.

Son olarak, bu adaptör sınıfını kullanarak FileStream’ı ILogger arayüzüyle kullanabiliriz:

class Program
{
    static void Main(string[] args)
    {
        // Adaptörü kullanarak FileStream'ı ILogger arayüzüyle kullanma
        ILogger logger = new FileStreamAdapter("log.txt");
        logger.Log("Log this message");

        // Diğer ILogger implementasyonları da burada kullanılabilir
    }
}

Bu kod örneği, FileStream sınıfını ILogger arayüzüyle uyumlu hale getiren bir adaptör kullanarak bir dosyaya loglama işlemi gerçekleştirir. Bu sayede, FileStream sınıfı ILogger arayüzünü uygulamış gibi davranır ve mevcut ILogger uygulamaları ile birlikte kullanılabilir.

Bu örnek, Adapter Pattern’ın gücünü gösterir ve farklı arayüzler arasında geçiş yapmanın gerektiği durumlarda nasıl kullanılabileceğini açıklar. Adapter Pattern, mevcut kodun değiştirilmesini önlerken, farklı arayüzler arasında uyumlu bir köprü oluşturmayı sağlar.

ViewModel Eşlemesi İçin Adapter Pattern

Adapter Pattern, farklı arayüzler arasında uyumsuzluk olduğunda bu arayüzleri birbirine bağlamak için kullanılan bir yapısal tasarım desenidir. ViewModel mapping için Adapter Pattern kullanılması durumunda, genellikle farklı veri modelleri veya sınıflar arasındaki uyumsuzluğu çözmek için kullanılır.

Bir uygulama geliştiricisi, MVC (Model-View-Controller) veya MVVM (Model-View-ViewModel) gibi tasarım kalıplarını kullanırken, sıklıkla veri modeli sınıfları ve kullanıcı arayüzü (UI) sınıfları arasında uyumsuzluklarla karşılaşır. Özellikle, veri modeli sınıfları genellikle iş mantığını veya veritabanı şemasını yansıtırken, kullanıcı arayüzü sınıfları, kullanıcı etkileşimi ve görsel sunumla ilgili verileri içerebilir.

Adapter Pattern, bu iki farklı yapı arasında uyumsuzlukları gidermek için ideal bir çözüm sunar. ViewModel mapping için Adapter Pattern kullanırken genellikle şu adımları izleriz:

  1. ViewModel (Görünüm Modeli) Sınıflarının Oluşturulması:
    • Kullanıcı arayüzünde gösterilecek verileri temsil etmek için ViewModel sınıfları oluşturulur. Bu sınıflar, genellikle kullanıcı arayüzünün ihtiyaç duyduğu verileri içerir.
  2. Adapter Sınıfının Oluşturulması:
    • ViewModel sınıflarını doldurmak için veri modeli sınıflarından veri almak ve uygun bir şekilde dönüştürmek için bir adapter sınıfı oluşturulur. Bu adapter sınıfı, ViewModel sınıfının gereksinimlerine uygun veri sağlar.
  3. Adapter Sınıfının Kullanılması:
    • Kullanıcı arayüzü kodu, ViewModel sınıflarını kullanır. Adapter sınıfı, bu ViewModel sınıflarını doldurmak için gerekli veriyi sağlar. Arayüz kodu, veri modeli sınıflarından bağımsız hale gelir ve ViewModel sınıflarını kullanarak çalışır.

Örnek bir senaryo düşünelim: Bir e-ticaret uygulamasında, bir ürünün detaylarını göstermek için bir ViewModel sınıfına ihtiyacımız var. Ancak, veritabanında ürün bilgilerini temsil eden bir veri modeli sınıfı bulunuyor. Bu durumda, Adapter Pattern kullanarak bu iki yapı arasındaki uyumsuzluğu gideririz.

// Veritabanı sınıfı
public class ProductModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Description { get; set; }
    // Diğer özellikler...
}

// ViewModel sınıfı
public class ProductViewModel
{
    public string Name { get; set; }
    public string Price { get; set; }
    // Diğer özellikler...
}

// Adapter sınıfı
public class ProductAdapter
{
    public ProductViewModel Adapt(ProductModel model)
    {
        return new ProductViewModel
        {
            Name = model.Name,
            Price = model.Price.ToString("C2"),
            // Diğer özelliklerin dönüştürülmesi...
        };
    }
}

Bu örnekte, ProductAdapter sınıfı, ProductModel’den ProductViewModel’e dönüşümü gerçekleştirir. Böylece, kullanıcı arayüzü kodu, ViewModel sınıflarını kullanarak verileri doğrudan gösterebilir ve Adapter sınıfı, veri modeli sınıflarından gelen veriyi ViewModel sınıflarına uygun hale getirir. Bu sayede, ViewModel’ler veritabanı yapılarından bağımsız hale gelir ve kullanıcı arayüzü kodunun daha modüler ve esnek olmasını sağlar.

Observer

Observer Pattern Tanımı

Observer Pattern, bir nesnenin durumunda değişiklik olduğunda, bağımlı olan diğer nesnelere bu değişiklikleri bildirmek için kullanılan bir tasarım desenidir. Bu desen, yayınlayıcı-tüketici modeli olarak da bilinir ve sıklıkla yayın/abone veya gözlemci/abone modeli olarak adlandırılır.

Observer Pattern’ın temelinde bir yayınlayıcı (subject) ve bir veya daha fazla gözlemci (observer) bulunur. Yayınlayıcı, gözlemcilerine kendi durumu hakkında bilgi verir ve durumunda bir değişiklik olduğunda onları günceller. Gözlemciler, yayınlayıcının değişikliklerini takip eder ve gerekli aksiyonları alırlar.

Bu desenin ana bileşenleri şunlardır:

  1. Subject (Yayınlayıcı):
    • Yayınlayıcı, gözlemcilerin kayıt olduğu ve durumunu gözlemcilere bildirdiği ana nesnedir. Yayınlayıcı, bir arayüze sahip olabilir ve gözlemcilere kayıt olmalarını, ayrılmalarını ve kendisini güncellemelerini sağlayan yöntemleri sağlar.
  2. Observer (Gözlemci):
    • Gözlemci, yayınlayıcının durumunu izleyen ve değişiklikler olduğunda güncelleme alacak olan nesnedir. Gözlemciler, genellikle bir arayüze sahip olur ve yayınlayıcıdan güncelleme almak için bu arayüzü uygularlar.

Observer Pattern’ın uygulanması, genellikle aşağıdaki adımları içerir:

  • Yayınlayıcı sınıfı, gözlemcilerin kaydını tutar, onlara bildirim gönderir ve durumunda değişiklik olduğunda onları günceller.
  • Gözlemci sınıfları, yayınlayıcıya kaydolur ve yayınlayıcının durum değişikliklerine tepki verir.

Observer Pattern, MVC (Model-View-Controller), MVVM (Model-View-ViewModel) gibi tasarım kalıplarında sıklıkla kullanılır. Özellikle kullanıcı arayüzü bileşenlerinin durumunu izlemek ve güncellemek için ideal bir desendir. Örneğin, bir modelin durumu değiştiğinde, bu değişikliklerin otomatik olarak kullanıcı arayüzüne yansıtılması için Observer Pattern kullanılabilir.

Bu desenin uygulama alanları şunlardır:

  • MVC veya MVVM gibi model tabanlı tasarım kalıpları.
  • GUI (Graphical User Interface) uygulamaları.
  • Olay tabanlı programlamada (event-driven programming).
  • Veri yayınlama sistemleri.
  • Otomasyon sistemleri ve kontrol sistemleri gibi durum izleme gerektiren uygulamalar.

Implementation of Simple Event Delegate Observer in C#


Observer Pattern’ı uygulamak için C#’da basit bir olay (event) ve delegeler kullanarak bir gözlemci (observer) yapılandırabiliriz. Bir yayınlayıcı (subject) nesne, gözlemcilere (observer) kaydolabilir ve durumu değiştiğinde gözlemcilere haber verebilir.

Bu kavramı basit bir örnekle açıklayalım:

Öncelikle, bir yayınlayıcı (subject) sınıfı oluşturalım:

using System;

public class Subject
{
    // Olay (event) ve delegeleri tanımlayalım
    public event EventHandler StateChanged;
    
    // Yayınlayıcının durumu
    private string state;

    public string State
    {
        get { return state; }
        set
        {
            state = value;
            // Durum değiştiğinde olayı tetikleyelim
            OnStateChanged();
        }
    }

    // Olayı tetikleyen yöntem
    protected virtual void OnStateChanged()
    {
        StateChanged?.Invoke(this, EventArgs.Empty);
    }
}

Bu sınıfta, StateChanged adında bir olay (event) ve StateChanged adında bir delegede tanımladık. State adında bir özellik tanımladık ve bu özellik değiştiğinde OnStateChanged metodunu çağırarak olayı tetikler.

Şimdi, bir gözlemci (observer) sınıfı oluşturalım:

using System;

public class Observer
{
    public void Subscribe(Subject subject)
    {
        // Yayınlayıcıdaki olaya abone olalım
        subject.StateChanged += HandleStateChanged;
    }

    public void Unsubscribe(Subject subject)
    {
        // Aboneliği iptal edelim
        subject.StateChanged -= HandleStateChanged;
    }

    private void HandleStateChanged(object sender, EventArgs args)
    {
        // Yayınlayıcının durumu değiştiğinde bu metod tetiklenecek
        Console.WriteLine("State changed!");
    }
}

Bu sınıf, Subscribe ve Unsubscribe adında iki yöntem içerir. Subscribe yöntemi, bir yayınlayıcıdaki olaya abone olurken Unsubscribe yöntemi ise aboneliği iptal eder. HandleStateChanged yöntemi, yayınlayıcının durumu değiştiğinde tetiklenecek olan yöntemdir.

Şimdi, bu sınıfları kullanarak bir örnek oluşturalım:

class Program
{
    static void Main(string[] args)
    {
        // Yayınlayıcı ve gözlemci oluşturalım
        Subject subject = new Subject();
        Observer observer = new Observer();

        // Gözlemci, yayınlayıcıdaki olaya abone olsun
        observer.Subscribe(subject);

        // Yayınlayıcının durumunu değiştirelim
        subject.State = "New State";

        // Aboneliği iptal edelim
        observer.Unsubscribe(subject);

        // Yayınlayıcının durumunu tekrar değiştirelim
        subject.State = "Another State";
    }
}

Bu örnekte, bir yayınlayıcı ve bir gözlemci oluşturduk. Gözlemci, yayınlayıcıdaki olaya abone oldu ve yayınlayıcının durumu değiştiğinde haberdar oldu. Daha sonra, aboneliği iptal ettik ve tekrar durumu değiştirdiğimizde gözlemcinin bu değişiklikten haberi olmadı. Bu, Observer Pattern’ın basit bir uygulamasını göstermektedir.

Utilizing INotifyPropertyChanged for Model Change Observation in C#

INotifyPropertyChanged arabirimi (interface), C# ve .NET platformunda model sınıflarının durum değişikliklerini gözlemlemek için kullanılan bir mekanizmadır. Bu arabirim, Observer Pattern’ın bir uygulaması olarak düşünülebilir ve genellikle MVVM (Model-View-ViewModel) mimarisinde kullanılır. MVVM’de, View (kullanıcı arayüzü), ViewModel’lerin durum değişikliklerini dinler ve bu değişiklikleri kullanıcı arayüzünde günceller.

INotifyPropertyChanged arabirimi, .NET Framework’un System.ComponentModel ad alanında bulunur. Bu arabirim, sınıfın durumunda bir değişiklik olduğunda bir olay (event) fırlatmak için bir mekanizma sağlar. Bu olay, nesnenin durumu değiştiğinde tüketicilere (gözlemcilere) haber verilmesini sağlar.

INotifyPropertyChanged arabiriminin temel metodu PropertyChanged olayını tanımlar. Bu olay, sınıfın bir özelliği değiştiğinde tetiklenir ve bu değişikliği dinleyen tüm tüketicilere haber verilir.

Aşağıda, INotifyPropertyChanged arabirimini kullanarak bir model sınıfının durum değişikliklerini nasıl gözlemleyebileceğinizi gösteren bir örnek var:

using System;
using System.ComponentModel;

public class Person : INotifyPropertyChanged
{
    private string name;

    // Ad özelliği
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                // PropertyChanged olayını tetikleyelim
                OnPropertyChanged("Name");
            }
        }
    }

    // PropertyChanged olayı tanımlama
    public event PropertyChangedEventHandler PropertyChanged;

    // PropertyChanged olayını tetikleyen yardımcı metot
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Model nesnesi oluşturalım
        Person person = new Person();
        // PropertyChanged olayına abone olalım
        person.PropertyChanged += HandlePropertyChanged;

        // Modelin durumunu değiştirelim
        person.Name = "Alice";
    }

    // PropertyChanged olayını dinleyen metot
    static void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine($"Property '{e.PropertyName}' changed");
    }
}

Bu örnekte, Person sınıfı INotifyPropertyChanged arabirimini uygular. Name özelliğinin set erişimcisinde, özelliğin değeri değiştiğinde OnPropertyChanged metodu çağrılır ve PropertyChanged olayı tetiklenir. Main metodu içinde, Person nesnesinin PropertyChanged olayına abone oluruz. Ardından, Name özelliğinin değerini değiştirdiğimizde, bu değişikliğin dinlendiği HandlePropertyChanged metodu çağrılır.

INotifyPropertyChanged arabirimi, genellikle kullanıcı arayüzü bileşenlerinin (örneğin, WPF veya WinForms gibi) ViewModel sınıflarının durum değişikliklerini dinlemek için kullanılır. Bu arabirim, bir nesnenin durum değişikliklerini gözlemlemek ve bu değişiklikleri diğer bileşenlere (örneğin, kullanıcı arayüzü) iletmek için mükemmel bir mekanizma sağlar. Bu sayede, MVVM modeli içinde model sınıflarının ve kullanıcı arayüzünün (View) birbirinden bağımsız hale gelmesi sağlanır.

Builder

Builder Pattern Tanımı


Builder Pattern, nesne oluşturma sürecini ayrı bir sınıf üzerinden yürüten ve karmaşık nesnelerin adım adım oluşturulmasını sağlayan bir tasarım desenidir. Builder Pattern, nesne oluşturma sürecini bir yapılandırma adım kümesine böler ve bu adımların sırasını ve gerçekleştirilmesini kontrol eder. Bu sayede, karmaşık nesnelerin oluşturulması daha modüler ve esnek hale gelir.

Builder Pattern’ın temel amacı, bir nesnenin oluşturulması için gerekli adımları kapsüle etmektir. Bu desen genellikle karmaşık nesnelerin oluşturulması sırasında kullanılır ve aşağıdaki durumlar için uygundur:

  1. Nesne oluşturma süreci karmaşık ve adım adım gerçekleşmelidir.
  2. Aynı oluşturma işlemlerinin farklı adımlarla yapılandırılması gerekebilir.
  3. Nesne oluşturma süreci farklı varyasyonlara sahip nesnelerin oluşturulmasını sağlamalıdır.

Builder Pattern, genellikle bir ürün sınıfı ve bu ürünün oluşturulması için bir builder sınıfı olmak üzere iki temel bileşenden oluşur:

  1. Product (Ürün):
    • Oluşturulacak karmaşık nesneyi temsil eder. Builder Pattern, bu ürünün oluşturulması için adım adım bir yapı sağlar.
  2. Builder:
    • Ürün nesnesinin oluşturulması için gerekli adımları tanımlar ve bu adımların gerçekleştirilmesinden sorumludur. Builder sınıfı, genellikle soyut bir sınıf veya bir arayüz olarak tanımlanır ve ardından farklı varyasyonlara sahip ürünlerin oluşturulmasını sağlamak için bu soyut sınıfı uygulayan farklı concrete builder sınıfları oluşturulabilir.

Builder Pattern, nesne oluşturma sürecini müşteriden gizler ve nesnenin nasıl oluşturulduğu ile ilgilenmez. Bu, karmaşık nesnelerin oluşturulması sürecini kolaylaştırır ve genellikle nesnelerin değişen yapısına uyum sağlamak için kullanılır.

Özetle, Builder Pattern, karmaşık nesnelerin adım adım oluşturulması ve bu oluşturma sürecinin yapısal bir şekilde yönetilmesi için kullanılan bir tasarım desenidir. Bu desen, nesne oluşturma sürecini modüler ve esnek hale getirir, böylece farklı varyasyonlara sahip nesnelerin oluşturulmasını sağlar.

Basic Builder Pattern Implementation in C#

Builder Pattern’ın C# dilinde basit bir uygulamasını oluşturalım. Bu örnekte, bir pizza siparişi oluşturmak için Builder Pattern kullanacağız.

İlk adım olarak, bir pizza sınıfı oluşturalım:

public class Pizza
{
    public string Size { get; set; }
    public bool Cheese { get; set; }
    public bool Pepperoni { get; set; }
    public bool Jalapenos { get; set; }

    public override string ToString()
    {
        return $"Pizza: {Size}, Cheese: {Cheese}, Pepperoni: {Pepperoni}, Jalapenos: {Jalapenos}";
    }
}

Şimdi, Builder Pattern’ı uygulamak için bir pizza yapıcı (builder) sınıfı oluşturalım:

public class PizzaBuilder
{
    private Pizza pizza;

    public PizzaBuilder(string size)
    {
        pizza = new Pizza { Size = size };
    }

    public PizzaBuilder AddCheese(bool cheese)
    {
        pizza.Cheese = cheese;
        return this;
    }

    public PizzaBuilder AddPepperoni(bool pepperoni)
    {
        pizza.Pepperoni = pepperoni;
        return this;
    }

    public PizzaBuilder AddJalapenos(bool jalapenos)
    {
        pizza.Jalapenos = jalapenos;
        return this;
    }

    public Pizza Build()
    {
        return pizza;
    }
}

Builder sınıfımız, pizza nesnesinin oluşturulması için gerekli adımları tanımlar. Her adım, builder sınıfından çağrılabilir ve builder sınıfı kendisini döndürerek zincirleme bir arayüz sağlar.

Son olarak, Builder Pattern’ı kullanarak bir pizza oluşturalım:

class Program
{
    static void Main(string[] args)
    {
        // Pizza yapıcı (builder) nesnesi oluşturalım
        PizzaBuilder builder = new PizzaBuilder("Large");
        
        // Pizza nesnesini oluşturalım
        Pizza pizza = builder.AddCheese(true)
                             .AddPepperoni(true)
                             .AddJalapenos(false)
                             .Build();

        // Pizza nesnesini yazdıralım
        Console.WriteLine(pizza);
    }
}

Bu örnekte, bir pizza nesnesi oluşturmak için Builder Pattern’ı kullandık. Pizza yapıcı (builder) nesnesi oluşturulduktan sonra, pizza özellikleri (peynir, pepperoni, jalapenos vb.) zincirleme olarak eklenir. Son olarak, Build metodu çağrılarak pizza nesnesi oluşturulur.

Builder Pattern, karmaşık nesnelerin oluşturulması sürecini yönetmek için kullanışlı bir tasarım desenidir. Bu desen, nesne oluşturma sürecini modüler hale getirir ve farklı varyasyonlara sahip nesnelerin oluşturulmasını kolaylaştırır.

Exploring the ApplicationBuilder Pattern in an Enterprise MVC Application


ApplicationBuilder
deseni, bir uygulamanın yapılandırma ve başlatma süreçlerini yönetmek için kullanılan bir tasarım desenidir. Bu desen genellikle büyük ölçekli veya kurumsal düzeydeki MVC (Model-View-Controller) veya Web API uygulamalarında kullanılır. ApplicationBuilder deseni, uygulama başlatma sürecini parçalara ayırarak, yapılandırılabilir ve genişletilebilir bir yapı oluşturmayı amaçlar.

Bir kurumsal MVC uygulamasında ApplicationBuilder deseninin nasıl kullanılabileceğini anlamak için aşağıdaki temel bileşenleri ele alabiliriz:

  1. ApplicationBuilder (Uygulama Oluşturucu):
    • Bu, uygulamanın başlatılması ve yapılandırılması için ana yapıyı sağlar. ApplicationBuilder sınıfı, başlatma sürecini kontrol eden yöntemleri içerir. Bu yöntemler, arka planda farklı bileşenlerin yüklenmesi, yapılandırılması ve başlatılması gibi işlemleri gerçekleştirir.
  2. Middleware (Ara Yazılım):
    • Middleware’ler, HTTP istekleri ve yanıtlarının işlenmesini sağlar. Her bir middleware, gelen isteği işleyebilir, isteği başka bir middleware’e yönlendirebilir veya isteği işlemeyi sonlandırabilir. Örneğin, kimlik doğrulama, günlükleme, oturum yönetimi gibi ortak işlevler middleware’ler aracılığıyla sağlanabilir.
  3. IServiceProvider (Hizmet Sağlayıcı):
    • ApplicationBuilder, uygulama başlatma sürecinde kullanılacak hizmetlerin yönetimini sağlar. IServiceProvider, uygulama içindeki hizmetlerin (servislerin) oluşturulması ve sağlanması için bir arayüz sağlar. Bu, bağımlılık enjeksiyonu (dependency injection) ve servis bağlamı (service container) gibi kavramları içerir.
  4. IApplicationBuilderExtensions (Uygulama Oluşturucu Uzantıları):
    • Bu, ApplicationBuilder sınıfını genişletmek için kullanılan uzantı yöntemlerini içerir. Özel middleware’lerin eklenmesi, yapılandırılması veya yönetilmesi gibi işlevler burada tanımlanabilir.
  5. Startup (Başlangıç):
    • Startup sınıfı, uygulamanın başlatılması ve yapılandırılması için gereken tüm kodu içerir. Startup sınıfı, uygulamanın yapılandırması, hizmetlerin kaydedilmesi, middleware’lerin eklenmesi ve başlatılması gibi adımları içeren ConfigureServices ve Configure yöntemlerini içerir.

Kurumsal bir MVC uygulamasında, ApplicationBuilder deseni, uygulamanın başlatılması ve yapılandırılmasını daha modüler ve yapılandırılabilir hale getirir. Bu desen, farklı ortamlar veya gereksinimler için uygulamanın başlatılma sürecini esnek hale getirir ve her bir adımın kontrolünü sağlar.

Özetle, ApplicationBuilder deseni, büyük ölçekli veya kurumsal düzeydeki MVC veya Web API uygulamalarında uygulamanın başlatılması ve yapılandırılmasını yönetmek için kullanılan bir tasarım desenidir. Bu desen, uygulamanın başlatılma sürecini modüler hale getirir, yapılandırılabilirliği artırır ve uygulama bileşenlerinin bağımlılıklarını azaltır.

Aşağıda C# dilinde ApplicationBuilder desenini kullanarak basit bir uygulamanın başlatılması ve yapılandırılması için kod örnekleri bulunmaktadır.

Öncelikle, ApplicationBuilder sınıfını ve Startup sınıfını tanımlayalım:

using System;

public class ApplicationBuilder
{
    private readonly IServiceProvider _serviceProvider;

    public ApplicationBuilder(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Start()
    {
        // Başlatma süreci
        Console.WriteLine("Application is starting...");
        var startup = (Startup)_serviceProvider.GetService(typeof(Startup));
        startup.ConfigureServices();
        startup.Configure();
        Console.WriteLine("Application has started.");
    }
}

public class Startup
{
    private readonly IServiceCollection _services;

    public Startup(IServiceCollection services)
    {
        _services = services;
    }

    public void ConfigureServices()
    {
        // Servislerin kaydedilmesi
        Console.WriteLine("Configuring services...");
        _services.AddSingleton<IMyService, MyService>();
    }

    public void Configure()
    {
        // Middleware'lerin eklenmesi
        Console.WriteLine("Configuring middleware...");
        var myService = (IMyService)_services.BuildServiceProvider().GetService(typeof(IMyService));
        myService.DoSomething();
    }
}

public interface IMyService
{
    void DoSomething();
}

public class MyService : IMyService
{
    public void DoSomething()
    {
        Console.WriteLine("Doing something...");
    }
}

Bu kod örneğinde, ApplicationBuilder ve Startup sınıfları tanımlanmıştır. ApplicationBuilder sınıfı, uygulamanın başlatılması için gereken adımları içerir. Startup sınıfı ise, uygulamanın yapılandırılması için gereken adımları içerir.

Son olarak, bu sınıfları kullanarak uygulamanın başlatılması işlemi gerçekleştirilir:

using Microsoft.Extensions.DependencyInjection;

class Program
{
    static void Main(string[] args)
    {
        // Hizmetlerin kaydedilmesi
        var services = new ServiceCollection();
        var serviceProvider = services.BuildServiceProvider();

        // ApplicationBuilder'ın oluşturulması ve başlatılması
        var appBuilder = new ApplicationBuilder(serviceProvider);
        appBuilder.Start();
    }
}

Bu kod örneğinde, IServiceCollection kullanarak uygulama hizmetlerinin kaydedilmesi gerçekleştirilir. Daha sonra, ApplicationBuilder sınıfı oluşturulur ve başlatılır. Başlatma işlemi sırasında Startup sınıfındaki ConfigureServices ve Configure yöntemleri sırasıyla çağrılır.

Bu şekilde, ApplicationBuilder desenini kullanarak bir uygulamanın başlatılması ve yapılandırılması işlemi yapılmış olur. Bu desen, uygulamanın başlatılma sürecini modüler hale getirir, yapılandırılabilirliği artırır ve uygulama bileşenlerinin bağımlılıklarını azaltır.

Implementing the Builder Pattern in an MVC Application

Builder deseninin MVC uygulamalarında kullanımını anlamak için, bir örnek uygulama oluşturabiliriz. Bu örnekte, bir MVC uygulamasında bir öğrenci kaydı oluşturma işlemini ele alacağız ve Builder desenini kullanarak bu işlemi gerçekleştireceğiz.

Öğrenci Sınıfı (Product):

  • Öğrenci sınıfı, öğrenci kayıtlarını temsil eden bir sınıf olacak.
public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Department { get; set; }
}

StudentBuilder Sınıfı (Builder):

  • StudentBuilder sınıfı, öğrenci nesnelerini adım adım oluşturacak olan sınıf olacak.
public class StudentBuilder
{
    private Student _student;

    public StudentBuilder()
    {
        _student = new Student();
    }

    public StudentBuilder WithName(string name)
    {
        _student.Name = name;
        return this;
    }

    public StudentBuilder WithAge(int age)
    {
        _student.Age = age;
        return this;
    }

    public StudentBuilder WithDepartment(string department)
    {
        _student.Department = department;
        return this;
    }

    public Student Build()
    {
        return _student;
    }
}

Öğrenci Kaydı Servisi (Director):

  • Öğrenci kaydı servisi, öğrenci nesnesi oluşturmak için Builder sınıfını kullanacak olan bir servis olacak.
public class StudentRegistrationService
{
    private readonly StudentBuilder _builder;

    public StudentRegistrationService(StudentBuilder builder)
    {
        _builder = builder;
    }

    public Student RegisterStudent(string name, int age, string department)
    {
        return _builder.WithName(name)
                       .WithAge(age)
                       .WithDepartment(department)
                       .Build();
    }
}

Kontrolör (Controller):

  • MVC uygulamasının kontrolörü, istemciden gelen istekleri işleyecek olan bileşen olacak.
public class StudentController : Controller
{
    private readonly StudentRegistrationService _registrationService;

    public StudentController(StudentRegistrationService registrationService)
    {
        _registrationService = registrationService;
    }

    [HttpPost]
    public IActionResult CreateStudent(StudentViewModel model)
    {
        var student = _registrationService.RegisterStudent(model.Name, model.Age, model.Department);
        // Öğrenci nesnesini veritabanına kaydet veya başka bir işlem yap
        return Ok(student);
    }
}

ViewModel (View Model):

  • View Model, istemci tarafından sunulan verileri tutan bir model olacak.
public class StudentViewModel
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Department { get; set; }
}

Bu şekilde, Builder desenini kullanarak bir MVC uygulamasında öğrenci kaydı oluşturma işlemini gerçekleştirebiliriz. StudentBuilder sınıfı öğrenci nesnelerini adım adım oluştururken, StudentRegistrationService servisi bu nesnelerin oluşturulma sürecini yönetir. Son olarak, StudentController sınıfı istemci isteklerini işleyerek öğrenci kaydı oluşturur. Bu tasarım, uygulamanın genişlemesini ve bakımını kolaylaştırır ve karmaşık öğrenci nesnelerinin oluşturulmasını basitleştirir.

Command

Command Pattern Tanımı

Command deseni, bir işlemi içeren bir nesneyi kapsülleyerek ve bu işlemi isteğe bağlı olarak yürüten bir yapı oluşturmayı amaçlar. Bu desen, bir işlemin yürütülme zamanını belirlerken, istemci kodunun ne zaman ve hangi işlemin gerçekleştirileceğini belirleme sorumluluğunu azaltır. Ayrıca, geri al (undo) işlemlerini desteklemek ve işlemleri sıraya almak gibi ekstra yetenekler de sağlar.

Command deseninin temel unsurları şunlardır:

  1. Command (Komut):
    • Uygulamanın bir parçası olarak yürütülecek bir işlemi temsil eder. Bu genellikle bir arabirim veya soyut sınıf olarak tanımlanır ve Execute metodu gibi bir işlemi yürütme yöntemini içerir.
  2. ConcreteCommand (Somut Komut):
    • Command arayüzünü veya soyut sınıfını uygular ve bir işlemi belirli bir alıcıda (receiver) gerçekleştirmek için gerekli mantığı sağlar.
  3. Receiver (Alıcı):
    • Bir işlemi gerçekten yürüten nesneyi temsil eder. Komut, alıcı üzerindeki bir veya daha fazla yöntemi çağırarak işlemi yürütür.
  4. Invoker (Çağırıcı):
    • Bir komutun yürütülmesi taleplerini alır ve bu talepleri gerçekleştirmek için gerekli komut nesnesini çağırır.
  5. Client (İstemci):
    • Bir veya daha fazla komut nesnesini oluşturan ve çağıran kod parçasıdır. İstemci, işlemleri oluşturur ve bu işlemleri yürütmek için çağırıcıya iletir.

Command deseni, işlemleri bir nesne aracılığıyla temsil ederek kodun daha modüler ve esnek olmasını sağlar. Ayrıca, geri al ve işlemleri sıraya alma gibi özellikleri kolayca uygulamayı mümkün kılar.

Özetle, Command deseni, bir işlemi içeren bir nesneyi kapsülleyerek ve bu işlemi isteğe bağlı olarak yürüten bir yapı oluşturur. Bu desen, uygulamanın modülerliğini artırır, geri al işlemlerini kolaylaştırır ve işlemleri sıraya alma gibi ekstra yetenekler sağlar.

Basic Implementation of the Command Pattern in C#

Command desenini basit bir şekilde C# ile nasıl uygulayabileceğimizi görelim.

Command (Komut): İşlemi temsil eden bir arayüz veya soyut sınıf olarak tanımlanır.

public interface ICommand
{
    void Execute();
}

ConcreteCommand (Somut Komut): Command arayüzünü uygular ve belirli bir işlemi gerçekleştirmek için gerekli mantığı sağlar.

public class ConcreteCommand : ICommand
{
    private Receiver _receiver;

    public ConcreteCommand(Receiver receiver)
    {
        _receiver = receiver;
    }

    public void Execute()
    {
        _receiver.Action();
    }
}

Receiver (Alıcı): Bir işlemi gerçekten yürüten nesneyi temsil eder.

public class Receiver
{
    public void Action()
    {
        Console.WriteLine("Receiver: Action executed");
    }
}

Invoker (Çağırıcı): Bir komutun yürütülme taleplerini alır ve bu talepleri gerçekleştirmek için gerekli komut nesnesini çağırır.

public class Invoker
{
    private ICommand _command;

    public void SetCommand(ICommand command)
    {
        _command = command;
    }

    public void ExecuteCommand()
    {
        _command.Execute();
    }
}

Client (İstemci): Komut nesnelerini oluşturur ve çağırıcıya ileterek işlemleri yürütür.

class Program
{
    static void Main(string[] args)
    {
        // Alıcı oluştur
        var receiver = new Receiver();

        // Komut oluştur ve alıcıyı belirt
        var command = new ConcreteCommand(receiver);

        // Çağırıcı oluştur ve komutu ayarla
        var invoker = new Invoker();
        invoker.SetCommand(command);

        // Komutu yürüt
        invoker.ExecuteCommand();
    }
}

Bu basit örnekte, Command desenini kullanarak bir işlemi temsil eden bir arayüz (ICommand) tanımladık. Ardından, bu arayüzü uygulayan bir ConcreteCommand sınıfı oluşturduk. Receiver sınıfı, gerçek işlemleri gerçekleştiren nesneyi temsil ederken, Invoker sınıfı komutları alır ve yürütür. Son olarak, Program sınıfında bu bileşenleri bir araya getirerek işlemi gerçekleştirdik.

Bu örnek, Command deseninin temel prensiplerini göstermektedir. Command deseni, işlemleri nesneler aracılığıyla temsil ederek kodun daha modüler ve esnek olmasını sağlar.

Implementing a Data Update Command in a C# MVC Application

Bir Command deseninin bir C# MVC uygulamasında nasıl kullanılabileceğini anlamak için, bir örneği ele alalım: Veri güncelleme işlemleri.

Command (Komut): Veri güncelleme işlemini temsil eden bir arayüz veya soyut sınıf olarak tanımlanır.

public interface ICommand
{
    void Execute();
}

ConcreteCommand (Somut Komut): Command arayüzünü uygular ve belirli bir veri güncelleme işlemini gerçekleştirmek için gerekli mantığı sağlar.

public class UpdateDataCommand : ICommand
{
    private readonly IDataService _dataService;
    private readonly int _id;
    private readonly string _newData;

    public UpdateDataCommand(IDataService dataService, int id, string newData)
    {
        _dataService = dataService;
        _id = id;
        _newData = newData;
    }

    public void Execute()
    {
        _dataService.UpdateData(_id, _newData);
    }
}

Receiver (Alıcı): Veri güncelleme işlemini gerçekleştiren hizmeti temsil eder.

public interface IDataService
{
    void UpdateData(int id, string newData);
}

public class DataService : IDataService
{
    public void UpdateData(int id, string newData)
    {
        // Veri güncelleme işlemini gerçekleştir
    }
}

Invoker (Çağırıcı): Bir komutun yürütülme taleplerini alır ve bu talepleri gerçekleştirmek için gerekli komut nesnesini çağırır.

public class CommandInvoker
{
    private ICommand _command;

    public void SetCommand(ICommand command)
    {
        _command = command;
    }

    public void ExecuteCommand()
    {
        _command.Execute();
    }
}

Kontrolör (Controller): Komut nesnelerini oluşturur ve Invoker’a ileterek işlemleri yürütür.

public class DataController : Controller
{
    private readonly IDataService _dataService;
    private readonly CommandInvoker _invoker;

    public DataController(IDataService dataService, CommandInvoker invoker)
    {
        _dataService = dataService;
        _invoker = invoker;
    }

    [HttpPost]
    public IActionResult UpdateData(int id, string newData)
    {
        var updateCommand = new UpdateDataCommand(_dataService, id, newData);
        _invoker.SetCommand(updateCommand);
        _invoker.ExecuteCommand();
        return RedirectToAction("Index");
    }
}

Bu örnekte, Command desenini kullanarak bir MVC uygulamasında veri güncelleme işlemini ele aldık. UpdateDataCommand sınıfı, veri güncelleme işlemini gerçekleştiren bir komut sınıfıdır. IDataService arayüzü, veri hizmeti tarafından sağlanan bir alıcıdır ve veri güncelleme işlemini gerçekleştirir. CommandInvoker sınıfı, komutların çağrılmasını ve yürütülmesini yönetir. Son olarak, DataController sınıfı, veri güncelleme işlemini başlatır ve Invoker’a ileterek yürütülmesini sağlar.

Bu şekilde, Command desenini kullanarak MVC uygulamanızda veri güncelleme gibi işlemleri modüler ve esnek bir şekilde yönetebilirsiniz. Bu desen, karmaşık işlemleri basitleştirmek ve uygulamanızın bakımını kolaylaştırmak için güçlü bir araçtır.

Proxy

Proxy Pattern Tanıımı

Proxy deseni, bir nesnenin yerel veya uzak bir kaynağa erişimini kontrol etmek için kullanılan bir yapıdır. Bu desen, gerçek nesnenin yerine geçerek araya girer ve istemcinin nesneye doğrudan erişimini engeller veya kontrol eder. Temel amaç, gerçek nesnenin yaratılmasını ve yüklenmesini geciktirmek, istemci ve gerçek nesne arasında bir arayüz sağlamak, nesne üzerinde yetkilendirme veya güvenlik kontrolleri uygulamak veya nesne üzerinde ekstra işlevsellik sağlamaktır.

Proxy deseninin ana bileşenleri şunlardır:

  1. Subject (Konu):
    • Gerçek nesneyi temsil eden bir arayüz veya soyut sınıftır. Proxy deseninde bu, istemcinin erişimine izin verilen veya kontrol edilen gerçek nesneye erişim sağlayan bir arayüzü tanımlar.
  2. RealSubject (Gerçek Konu):
    • Gerçek nesneyi temsil eden sınıftır. Bu sınıf, işlemlerin gerçekleştirildiği asıl iş mantığını içerir.
  3. Proxy (Vekil):
    • Gerçek nesnenin yerine geçen ve istemci tarafından doğrudan erişime izin verilen veya kontrol edilen bir aracıdır. Proxy, istemci tarafından gerçek nesneye yönlendirilmeden önce gerektiğinde ek işlevselliği sağlar veya gerçek nesnenin yaratılmasını ve yüklenmesini geciktirebilir.

Proxy deseninin uygulanması, aşağıdaki durumlarda faydalı olabilir:

  • Uzak bir kaynağa erişim gerekiyorsa (örneğin, ağ üzerindeki bir hizmet).
  • Bir nesnenin yaratılması veya yüklenmesi maliyetliyse ve bu işlem istemci tarafından gerektiğinde gerçekleştirilmelidir.
  • Gerçek nesneye erişimde yetkilendirme veya güvenlik kontrolleri gerekiyorsa.
  • Bir nesneye erişimin nasıl gerçekleştirileceğini veya kontrol edileceğini merkezi bir noktaya taşımak isteniyorsa.

Proxy deseni, gerçek nesneye erişimde bir ara katman sağlayarak uygulamayı modülerleştirmek, performansı artırmak ve güvenlik veya yetkilendirme kontrolleri uygulamak için ideal bir seçenektir.

Basic Proxy File Access in C#

Basit bir Proxy dosya erişimi örneği oluşturalım. Bu örnekte, bir dosyaya erişim sağlayan bir Proxy sınıfı ve gerçek dosya erişimini sağlayan bir RealSubject sınıfı oluşturacağız.

Subject (Konu): Dosyaya erişim için bir arayüz tanımlayalım.

public interface IFileAccess
{
    string ReadFileContent();
}

RealSubject (Gerçek Konu): Dosyaya gerçek erişimi sağlayan sınıfı tanımlayalım.

using System.IO;

public class RealFileAccess : IFileAccess
{
    private readonly string _filePath;

    public RealFileAccess(string filePath)
    {
        _filePath = filePath;
    }

    public string ReadFileContent()
    {
        return File.ReadAllText(_filePath);
    }
}

Proxy (Vekil): Dosyaya erişimi kontrol eden ve gerekirse ekstra işlemler ekleyen bir Proxy sınıfı oluşturalım.

public class FileProxy : IFileAccess
{
    private readonly string _filePath;
    private RealFileAccess _realFileAccess;

    public FileProxy(string filePath)
    {
        _filePath = filePath;
    }

    public string ReadFileContent()
    {
        // Gerçek dosya erişimi sadece ilk çağrıldığında gerçekleştirilir
        if (_realFileAccess == null)
        {
            _realFileAccess = new RealFileAccess(_filePath);
        }

        // Gerçek dosya erişimini çağırır ve sonucu döndürür
        return _realFileAccess.ReadFileContent();
    }
}

Kullanım:

class Program
{
    static void Main(string[] args)
    {
        // Proxy aracılığıyla dosyaya erişim
        IFileAccess fileProxy = new FileProxy("example.txt");

        // Dosya içeriğini oku
        string content = fileProxy.ReadFileContent();
        Console.WriteLine(content);
    }
}

Bu kod, Proxy desenini kullanarak bir dosyaya erişimi kontrol eden basit bir uygulamayı göstermektedir. RealFileAccess sınıfı gerçek dosya erişimini sağlarken, FileProxy sınıfı gerçek dosyaya erişimi kontrol eder. İlk çağrıldığında RealFileAccess sınıfı oluşturulur ve gerçek dosya erişimi gerçekleştirilir. Daha sonra aynı dosyaya erişim yapıldığında, gerçek dosya erişimi tekrar gerçekleşmez ve daha önce oluşturulmuş RealFileAccess nesnesi kullanılır. Bu şekilde, Proxy deseni gerçek dosya erişimini geciktirerek ve gerektiğinde ekstra işlevselliği sağlayarak dosya erişimini kontrol eder.

Implementing a Cached Data Service Proxy in C# MVC

Caching data service proxy, bir veri hizmetine erişim sağlayan ve verileri bir önbellekte saklayarak tekrar eden istekler için gereksiz veri tabanı erişimini azaltan bir yapıdır. Bu desen, performansı artırmak ve veritabanı üzerindeki yükü azaltmak için kullanışlıdır. MVC uygulamasında bir caching data service proxy uygulamak için aşağıdaki adımları takip edebiliriz:

IDataService Arayüzü: Veri hizmetine erişim sağlamak için bir arayüz tanımlayalım.

public interface IDataService
{
    string GetData(int id);
}

RealDataService Sınıfı: Gerçek veri hizmetine erişimi sağlayan bir sınıf oluşturalım.

public class RealDataService : IDataService
{
    public string GetData(int id)
    {
        // Veritabanından veri al
        return $"Data for ID {id} from database";
    }
}

CachingDataServiceProxy Sınıfı: Veri erişimini kontrol eden ve gerektiğinde önbellekteki veriyi kullanarak gerçek veri hizmetine erişim sağlayan bir proxy sınıfı oluşturalım.

using System;
using System.Collections.Generic;

public class CachingDataServiceProxy : IDataService
{
    private readonly IDataService _realDataService;
    private readonly Dictionary<int, string> _cache;

    public CachingDataServiceProxy(IDataService realDataService)
    {
        _realDataService = realDataService;
        _cache = new Dictionary<int, string>();
    }

    public string GetData(int id)
    {
        // Önbellekte veri varsa, önbellekten veriyi döndür
        if (_cache.ContainsKey(id))
        {
            return $"Data for ID {id} from cache: {_cache[id]}";
        }

        // Önbellekte veri yoksa, gerçek veri hizmetinden veriyi al ve önbelleğe ekle
        var data = _realDataService.GetData(id);
        _cache[id] = data;
        return data;
    }
}

Kontrolör (Controller): Veri hizmetine erişimi sağlayan bir kontrolör oluşturalım ve bu kontrolörde CachingDataServiceProxy kullanalım.

public class DataController : Controller
{
    private readonly IDataService _dataService;

    public DataController(IDataService dataService)
    {
        _dataService = new CachingDataServiceProxy(dataService);
    }

    public IActionResult GetData(int id)
    {
        var data = _dataService.GetData(id);
        return View((object)data);
    }
}

Bu şekilde, MVC uygulamasında bir caching data service proxy oluşturabiliriz. CachingDataServiceProxy sınıfı, gerçek veri hizmetine erişim sağlamak ve gerektiğinde önbellekteki veriyi kullanmak için kullanılır. Bu, veritabanı erişimini azaltarak uygulamanın performansını artırabilir ve veri tabanına olan yükü azaltabilir.

Chain of Responsibility

Chain of Responsibility Pattern Tanımı

Chain of Responsibility deseni, bir dizi nesnenin (handler) istemci tarafından gönderilen isteği işleme fırsatına sahip olduğu bir davranışsal desendir. İsteği işleyebilecek nesne bulunana kadar bu zincir boyunca isteğin ilerlediği bir yapıdır. Her bir handler, isteği işleyip işleyemeyeceğini belirler ve işleyebilecekse işler, aksi halde isteği bir sonraki handler’a iletir.

Bu desenin temel amacı, istemciden gelen istekleri işleyen nesneler arasında bağlantıları gevşek tutarak istemci kodunun daha modüler, esnek ve yeniden kullanılabilir olmasını sağlamaktır. Ayrıca, bir isteğin hangi nesne tarafından işleneceğinin belirlenmesini sağlayarak, işleme sırasında dinamik olarak değişiklik yapılmasına olanak tanır.

Chain of Responsibility deseninin ana bileşenleri şunlardır:

  1. Handler (İşleyici):
    • İstekleri işleyen arayüzü veya soyut sınıfı temsil eder. Bu sınıf, isteği işleyebilecek bir sonraki işleyiciyi belirler ve isteği işler veya bir sonraki işleyiciye iletir.
  2. ConcreteHandler (Somut İşleyici):
    • Handler arayüzünü veya soyut sınıfını uygular ve bir isteği işleyebilecek nesneyi temsil eder. Eğer işleyici isteği işleyebiliyorsa, işlemi gerçekleştirir; aksi halde isteği bir sonraki işleyiciye iletir.
  3. Client (İstemci):
    • Bir isteği gönderen ve hangi işleyicinin isteği işleyeceğini bilmeyen kod parçasıdır. İstemci, isteği işleyebilecek bir işleyici zincirini başlatır ve isteği zincir boyunca iletir.

Chain of Responsibility deseni, bir isteğin farklı işleyiciler tarafından farklı şekillerde işlenmesini sağlayarak, kodun daha modüler ve esnek olmasını sağlar. Ayrıca, yeni işleyiciler eklemek veya mevcut işleyicileri değiştirmek kolaydır, çünkü işleyici zinciri gevşek bağlıdır. Bu desen, özellikle isteğin işleyeceği işleyicinin dinamik olarak belirlendiği durumlarda faydalıdır.

Basic Exception Handling with Chain of Responsibility in C#

Chain of Responsibility desenini, basit bir örnek olan exception handling (istisna işleme) senaryosu üzerinden açıklayalım.

Handler (İşleyici): İstisnaları işleyen arayüzü veya soyut sınıfı temsil eder.

public interface IExceptionHandler
{
    void HandleException(Exception exception);
}

ConcreteHandler (Somut İşleyici): Handler arayüzünü veya soyut sınıfını uygular ve belirli bir türdeki istisnaları işleyebilecek nesneyi temsil eder.

public class LoggingExceptionHandler : IExceptionHandler
{
    public void HandleException(Exception exception)
    {
        // Hata günlüğüne istisnayı kaydet
        Console.WriteLine($"Logging Exception: {exception.Message}");
    }
}

public class EmailExceptionHandler : IExceptionHandler
{
    public void HandleException(Exception exception)
    {
        // E-posta göndererek istisnayı bildir
        Console.WriteLine($"Sending Email Exception: {exception.Message}");
    }
}

Client (İstemci): Bir istisnayı gönderen ve hangi işleyicinin istisnayı işleyeceğini bilmeyen kod parçasıdır. İstemci, istisnayı işleyebilecek bir işleyici zincirini başlatır ve istisnayı zincir boyunca iletir.

public class ExceptionHandlerChain
{
    private readonly List<IExceptionHandler> _handlers;

    public ExceptionHandlerChain()
    {
        _handlers = new List<IExceptionHandler>();
    }

    public void AddHandler(IExceptionHandler handler)
    {
        _handlers.Add(handler);
    }

    public void HandleException(Exception exception)
    {
        foreach (var handler in _handlers)
        {
            handler.HandleException(exception);
        }
    }
}

Kullanım:

class Program
{
    static void Main(string[] args)
    {
        // İşleyici zinciri oluştur
        var handlerChain = new ExceptionHandlerChain();
        handlerChain.AddHandler(new LoggingExceptionHandler());
        handlerChain.AddHandler(new EmailExceptionHandler());

        try
        {
            // Örnek bir istisna oluştur
            throw new InvalidOperationException("Invalid operation occurred!");
        }
        catch (Exception ex)
        {
            // İstisnayı işleyici zincirine ilet
            handlerChain.HandleException(ex);
        }
    }
}

Bu örnekte, Chain of Responsibility desenini kullanarak basit bir exception handling mekanizması oluşturduk. İstemci, bir istisna oluşturur ve bu istisnayı işleyebilecek bir işleyici zincirini başlatır. Zincirdeki her işleyici, istisnayı işlemek için sorumludur. Eğer bir işleyici istisnayı işleyemezse, istisna bir sonraki işleyiciye iletilecektir. Bu şekilde, istisnaların işlenmesi için esnek bir yapı oluşturulur ve her işleyici istisnayı farklı şekillerde işleyebilir veya işleyici zinciri kolayca değiştirilebilir.

Modeling ASP.NET Core Request Pipeline as a Chain of Responsibility

ASP.NET Core Request Pipeline’i Chain of Responsibility desenine benzer bir şekilde modelleyebiliriz. Bu, gelen HTTP isteğinin işlenmesi için bir dizi bileşenin (middleware) zincir halinde çalıştığı bir yapıya sahiptir. Her bir middleware, gelen isteği işler ve ardından isteği bir sonraki middleware’e iletir veya işlemi sonlandırır.

Handler (İşleyici): Middleware’leri temsil eder. Her bir middleware, gelen isteği işler ve isteği bir sonraki middleware’e iletir veya işlemi sonlandırır.

public interface IMiddleware
{
    Task InvokeAsync(HttpContext context, RequestDelegate next);
}

ConcreteHandler (Somut İşleyici): Middleware’leri temsil eden sınıflardır. Her bir middleware, IMiddleware arayüzünü uygular ve gelen isteği işler.

public class LoggingMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Gelen isteği işle
        Console.WriteLine($"Logging Middleware: {context.Request.Path}");

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

public class AuthenticationMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Gelen isteği doğrula
        Console.WriteLine($"Authentication Middleware: {context.Request.Path}");

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

public class AuthorizationMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Gelen isteği yetkilendir
        Console.WriteLine($"Authorization Middleware: {context.Request.Path}");

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

Client (İstemci): ASP.NET Core uygulamasının başlangıç noktası olan Startup sınıfı, middleware’leri configure eder ve bir pipeline oluşturur.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // Middleware'leri pipeline'a ekleyerek bir zincir oluştur
        app.UseMiddleware<LoggingMiddleware>();
        app.UseMiddleware<AuthenticationMiddleware>();
        app.UseMiddleware<AuthorizationMiddleware>();
        
        // Son middleware'yi eklemeyi unutma (isteği işlemeyi sonlandırır)
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

Bu şekilde, ASP.NET Core Request Pipeline’i, gelen HTTP isteğini işlemek için bir Chain of Responsibility desenine benzer bir yapı oluşturur. Her bir middleware, gelen isteği işler ve isteği bir sonraki middleware’e iletir. Bu sayede, işlem sırasında isteği farklı şekillerde işleyebilir ve middleware’lerin sırasını değiştirebiliriz.

Enhancing ASP.NET Core Chain of Responsibility with Filters

ASP.NET Core Request Pipeline’ına bir filtre eklemek için, mevcut Chain of Responsibility yapısına bir ara katman eklememiz gerekecektir. Bu ara katman, gelen isteği işleyecek ve bir sonraki middleware’e iletecek, ancak bir filtreleme işlemi de yapacaktır.

Handler (İşleyici): Middleware’leri temsil eden arayüz veya soyut sınıf. Tüm middleware’ler bu arayüzü uygular.

public interface IMiddleware
{
    Task InvokeAsync(HttpContext context, RequestDelegate next);
}

ConcreteHandler (Somut İşleyici): Middleware’leri temsil eden sınıflar. Her bir middleware, IMiddleware arayüzünü uygular ve gelen isteği işler.

public class LoggingMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Gelen isteği işle
        Console.WriteLine($"Logging Middleware: {context.Request.Path}");

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

public class AuthenticationMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Gelen isteği doğrula
        Console.WriteLine($"Authentication Middleware: {context.Request.Path}");

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

// Yeni eklenen filtre
public class CustomFilterMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // İsteği filtrele
        if (context.Request.Query.ContainsKey("filter"))
        {
            // Filtreleme işlemi
            Console.WriteLine($"Custom Filter Middleware: Filtering request by {context.Request.Query["filter"]}");
        }

        // İsteği bir sonraki middleware'e ilet
        await next(context);
    }
}

Client (İstemci): ASP.NET Core uygulamasının başlangıç noktası olan Startup sınıfı, middleware’leri configure eder ve bir pipeline oluşturur.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // Middleware'leri pipeline'a ekleyerek bir zincir oluştur
        app.UseMiddleware<LoggingMiddleware>();
        app.UseMiddleware<AuthenticationMiddleware>();

        // Yeni filtre middleware'ini pipeline'a ekleyin
        app.UseMiddleware<CustomFilterMiddleware>();

        // Son middleware'yi eklemeyi unutmayın (isteği işlemeyi sonlandırır)
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

Bu şekilde, ASP.NET Core Request Pipeline’ına bir filtre eklemiş olduk. Yeni eklenen CustomFilterMiddleware, gelen isteği filtreler ve filtreleme işlemi gerçekleştirir. Daha sonra, işlemi bir sonraki middleware’e iletir. Bu sayede, gelen isteği işlemek için özelleştirilmiş bir filtre ekleyebilir ve middleware zincirine entegre edebiliriz.

Sadece başarılı bir insan olmaya değil, değerli bir insan olmaya çalışın.

Albert Einstein

Bir sonraki yazıda görüşmek dileğiyle!”

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir


3 + 8 = ?