Derya Dok Kişisel Blog

Tag: c# Page 1 of 3

SOLID Yazılım Prensipleri – Dependency Inversion Principle

SOLID yazı dizisinin sonuncusu olan Dependency Inversion yazısından merhabalar! Bu yazıda sıklıkla Dependency Injection (DI) ile karıştırılan Dependency Inversion (DIP) prensibini inceleyeceğiz. Temelde “soyutlamaya bağlı olma” felsefesine dayanan bu prensip, sınıflar arasındaki sıkı bağımlılıkları (tight coupling) kırmayı hedefler. Yüksek bağımlılık; kodun bir parçasını diğerlerinden izole edememek ve değişime karşı direnç gösteremeyen bir yapı kurmak demektir.

Dependency Inversion Nedir?

Bu prensibi daha somut bir örnekle ele alalım: Bir lambayı evin elektrik tesisatına bağlamak istediğinizi hayal edin. Lambanın kablolarını duvarın içindeki elektrik hattına doğrudan bağlarsak, lambayı değiştirmek istediğimizde tüm duvarı kırmamız, kabloları kesmemiz ve yeni lambayı tekrar bağlamamız gerekir. Bu durum da aslında ‘Sıkı Bağımlılık’ (Tight Coupling) dediğimiz durumdur.

Oysa gerçek hayatta duvara bir priz (interface) koyuyoruz. Lamba, elektriğin kaynağına değil, o prize uyumlu olan fişe (abstraction) bağımlı oluyor. Dependency Inversion tam olarak budur:

  • Üst Seviye Modül (Lamba): Alt seviye modülün detaylarıyla (duvarın içindeki kablo tipi) ilgilenmez.
  • Alt Seviye Modül (Elektrik Tesisatı): Doğrudan lambaya bağlı değildir.
  • Soyutlama (Priz/Fiş): Her iki tarafın da üzerinde anlaştığı ortak standarttır.

Böylece priz standart kaldığı sürece, istersek lamba veya bilgisayarı şarja takabiliriz. Aynı şekilde kodunuzda bir değişikliğe gittiğinizde tüm sistemi bağımlılıklarından sökmenize gerek kalmaz.

Örnek olarak DIP kurallarına uymayan şu yapıyı inceleyelim;

internal class Order
{
    public Guid Id { get; set; }
}

internal class FileLogger
{
    public void Log(string message)
    {
        // Simulate logging to a file
        StreamWriter writer = new StreamWriter("log.txt");
        writer.WriteLine(message);
        writer.Flush();
        writer.Close();
    }
}

internal class DBLogger
{
    public void Log(string message)
    {
        SqlConnection conn = new SqlConnection();
        SqlCommand cmd = new SqlCommand("Insert", conn);
        conn.Open();
        cmd.ExecuteNonQuery();
        conn.Close();
    }
}

internal class OrderManager
{
    FileLogger fileLogger;
    DBLogger dbLogger;

    public OrderManager()
    {
        fileLogger = new FileLogger();
        dbLogger = new DBLogger();
    }

    public void Add(Order order)
    {
        // Add the order
        fileLogger.Log($"Order {order.Id} added.");
        dbLogger.Log($"Order {order.Id} added.");
    }

    public void Update(Order order)
    {
        // Update the order
        fileLogger.Log($"Order {order.Id} updated.");
        dbLogger.Log($"Order {order.Id} updated.");
    }

    public void Delete(Order order)
    {
        // Delete the order
        fileLogger.Log($"Order {order.Id} deleted.");
        dbLogger.Log($"Order {order.Id} deleted.");
    }
}

Örneğin bir sipariş yönetim sistemimiz olsun. Bu sistemde loglama işlemini iki farklı seviyede yapıyor olalım. Burada yaşayacağımız en büyük sorun, soyutlama (interface) olmadığı için OrderManager sınıfının loglama araçlarının somut detaylarına göbekten bağlı olmasıdır. Bu durum loglama kısmında yapılacak ufacık bir değişikliğin bile sistemi bozmasına neden olabilir. Öte yandan projenin ilerleyen fazlarında ADO.NET yerine Entity Framework’e geçiş yapılmak istediğimizde tüm DBLogger referanslarının düzenlenmesi gerekebilir.

Oysa ILogger şeklinde bir interface’e sahip olsaydık, bu interface’e loglama görevinin verildiği net bir şekilde anlardık. Loglama işleminin nasıl olacağına FileLogger ve DBLogger sınıfları içerisinde detaylandırabilirdik.

Dependency Inversion Nasıl Çalışır?

DIP’nin temel fikri; karmaşık bir mantık içeren üst seviye modülleri kolayca yeniden kullanılabilir olması ve alt seviyedeki modüllerde olabilecek değişikliklerden etkilenmemesidir. Bunu oluşturmak için üst ve alt seviye modülleri arasında bağımlılığı azaltacak bir soyutlama gerekir. Buna bağlı olarak DIP 2 aşamadan oluşur.

  1. Üst seviyede modüller alt seviye modüllere bağlı olmamalıdır; her ikisi de soyutlamalara bağlı olmalıdır.
  2. Soyutlamalar detaylara bağımlı olmamalıdır; detaylar soyutlamaya bağımlı olmalıdır.
internal class Order
{
    public Guid Id { get; set; }
}

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

internal class FileLogger : ILogger
{
    public void Log(string message)
    {
        StreamWriter writer = new StreamWriter("log.txt");
        writer.WriteLine(message);
        writer.Flush();
        writer.Close();
    }
}

internal class DBLogger : ILogger
{
    public void Log(string message)
    {
        SqlConnection conn = new SqlConnection();
        SqlCommand cmd = new SqlCommand("Insert", conn);
        conn.Open();
        cmd.ExecuteNonQuery();
        conn.Close();
    }
}

internal class OrderManager
{
    public ILogger _logger;

    public OrderManager(ILogger logger)
    {
        _logger = logger;
    }

    public void Add(Order order)
    {
        // Add the order
        _logger.Log($"Order {order.Id} added.");
    }

    public void Update(Order order)
    {
        // Update the order
        _logger.Log($"Order {order.Id} updated.");
    }

    public void Delete(Order order)
    {
        // Delete the order
        _logger.Log($"Order {order.Id} deleted.");
    }
}


OrderManager artık hangi loglama metodunun çağırıldığını ve o metodun nasıl çalıştığını bilmiyor sadece bir ILogger istiyor. Bu sayede hem manager sınıf içerisindeki tekrar eden kod yapısını azalttık, hem de ileride yeni bir loglama metodu eklenir veya varolan metot güncellenirse bu sınıf içerisinde bir değişiklik yapılmasını engelledik.

Bu yaklaşımla 3 önemli kazanım elde etmiş olduk.

  • Esneklik: İlerleyen fazlarda farklı bir loglama metodu eklemek istersek OrderManager’a dokunmamıza gerek kalmayacak. (Open/Closed)
  • Test Edilebilirlik: Unit test yazarken gerçek bir veri tabanı yerine kolayca mock bir logger entegre edebiliriz.
  • Güven: Tüm logger sınıflarımız ILogger interface’ine bağlı kaldığı için birbirlerinin yerine kullanılabilirler. (Liskov Substition)

SOLID serimin bu son yazısıyla, daha sürdürülebilir ve esnek kod temelleri atmanın yollarını incelemiş olduk. SOLID’in diğer kurallarını blog yazılarımdan okuyabilir, kullandığım örnekleri Github hesabımdan inceleyebilirsiniz. Bir sonraki yazıya kadar kendinize çok iyi bakın! 😊

SOLID Yazılım Prensipleri – Liskov Substitution Principle

Herkese merhaba. Uzun bir aradan sonra SOLID ilkesinin üçüncü prensibi ile bu yazı dizisine devam ediyorum. Liskov Substitution prensibi özetle kodlarımızda herhangi bir değişiklik yapmaya gerek duyulmadan türetilen sınıfların yerine kullanabilmesini ele alır. Bu sayede codebase içerisinde sağlam bir inheritance yapısı oluşur. Yanlış soyutlamaların önüne geçildiği için geliştirme yapılırken kodun tahmin edilebilirliği artar. Ayrıca bir önceki yazımda bahsettiğim Open Closed ilkesinin de öncül şartıdır.

SignalR Nedir?

Herkese merhaba. Bu yazıda gerçek zamanlı uygulama geliştirmek için kullanılan kütüphanelerden birisi olan SignalR kütüphanesini inceleyeceğiz. Gerçek zamanlı web, mobil uygulamaları artık günlük hayatımızın büyük bir kısmını oluşturmakta. Bu yüzden gerçek zamanlı uygulamaların teknoloji sektöründeki popülerliği hala devam ediyor. Anlık mesajlaşma, döviz takip, maç sonucu takip uygulamaları ve oyunlar gibi pek çok farklı sektörde kullanılan bu mekanizma aslında WebSocket ve RPC teknolojisine dayanmaktadır. Bu nedenle öncelikle WebSocket ve RPC yapılarına ardından da SignalR konusuna geçiş yapalım.

Design Patterns (Tasarım Kalıpları)

Herkese merhaba. Bu yazıda yazılım dünyasında oldukça sık kullanılan terimlerden biri olan Design Patterns (Tasarım Kalıpları) konusunu inceleyeceğiz. Design pattern’lar genellikle yazılım geliştirme sürecinde sıkça karşılaştığımız sorunlara karşı geliştirilen çözümlerdir. Pattern’ler kodun okunabilirliğini arttırır ve tekrar tekrar kullanılabilir olmasını sağlar. Pattern kavramı ilk olarak Cristopher Alexander’ın “A Pattern Language: Towns, Buildings, Construction” kitabında tanımlanmıştır. Kitap içerisinde kentsel çevreyi tasarlamak için kullanılan bir “dil” anlatılmaktadır. Kullanılan bu dilin birimlerini ise pattern’ler oluşturur. Bir binanın penceresinin yüksekliği, kaç kata sahip olması gerektiği gibi birim kurallar pattern’ler ile açıklanmaktadır. Daha sonra 1994’te Gang of Four (GoF) olarak bilinen 4 arkadaşın “Design Patterns – Elements of Reusable Object-Oriented Software” kitabı ile yazılım dünyası da pattern’ler ile tanışır.

C# Notlarim 25: Delegate ve Event Yapısı

Herkese merhaba. Bu yazıda kullanıcı etkileşimli programlama yaparken oldukça sık karşımıza çıkan delegate ve event konularını inceleyeceğiz. Bu ders için hazırladığım örnekleri Windows Forms projesi ile hazırladım. Daha öncesinde LINQ kullandıysanız Where() ya da Select() gibi metotların içerisine yazdığımız lambda expressionların da delegate yapısını kullandığını görmüşsünüzdür. İlerleyen yazılarda o konulara da değineceğiz. Daha fazla kafa karıştırmadan konuyu incelemeye başlayalım. Yazıda bulunan örneklere github hesabımdan ulaşabilirsiniz.

C# Notlarım 24: Generic Tipler

Herkese merhaba. Bu yazıda pek çok gelişmiş programlama dilinde bulunan bir konuyu, generic tipler konusunu ele alacağız. C# içerisinde kullanabileceğimiz tiplerin üzerine kendi oluşturduğumuz tipleri ekleyebileceğimizi nesne yönelimli programlamada görmüştük. Kendi oluşturduğuğumuz farklı tipleri ortak bir metot için kullanmak istiyorsak, metot parametresine object tipi verebiliriz. Bu yöntem kısa vadede sorunlarımızı çözüyor gibi görünse de uzun vadede bazı sıkıntılara yol açıyor. Yazıda bulunan örneklere github hesabımdan ulaşabilirsiniz.

C# Notlarım 23: Static Class Yapısı

Herkese merhaba. Bir önceki yazımda değer ve referans tipleri anlatmıştım. Bu yazıda da static class yapısını inceleyeceğiz. C# içerisinde nesne yönelimli programlamada static class’ları kullanarak daha fonksiyonel bir programlama yapabiliriz. Genellikle tek bir tane üretmek istediğimiz nesnelerde static yapısını tercih ederiz. Örneklere github hesabımdan ulaşabilirsiniz.

C# Notlarım 22: Değer ve Referans Tipler

Herkese merhaba. C# üzerinde bool, integer, string gibi pek çok değişken türü sayabiliriz. Bu değişken türleri aslında değer (value) ya da referans (reference) tip dediğimiz veri tiplerinden türerler. Değer ve referans tipler bellekte farklı bölgelerde tutulmaktadır. .NET Framework bellek yönetimini önemli ölçüde kendisi yapar. Biz de tanımladığımız değişkenin hangi bölgede yer almasını istiyorsak bellek yönetimini belli ölçülerde kendimiz ayarlamış oluruz. C# üzerindeki bellek bölgelerini inceleyerek konumuza giriş yapalım. Örneklere github hesabımdan ulaşabilirsiniz.

C# Notlarım 21: Abstract ve Interface

Herkese merhaba. Bir önceki yazımda abstraction konusunu detaylı bir şekilde anlatmıştım. Bu yazıda, abstract class ve interface arasındaki farkları inceleyeceğiz. İki yöntem de kullanım açısından açısından benzer olsa da aralarında bazı farklılıklar bulunmaktadır. İki yöntemi de ayrı ayrı başlıklar altında inceleyelim. Örneklere github hesabımdan ulaşabilirsiniz.

C# Notlarım 19: Çok Biçimlilik (Polymorphism)

Herkese merhaba. Nesne yönelimli programlamanın ana başlıklarından biri olan polymorphism konusunu inceleyeceğiz. Bazen içeride farklı işlemler yapılsa bile aynı sonucu döndüren metotlara ihtiyacımız olur. Aynı kodu tekrar tekrar yazmak bir süre sonra projenin inanılmaz boyutlara ulaşmasına neden olur. Nesne yönelimli programa metodolojisinde de bu sorunu çözmek için çok biçimlilikten faydalanırız. Çok biçimlilik, temelde aynı tür olan metotların farklı davranması gerektiğinde kullanıldığımız bir yöntemdir. Örneklere github reposundan ulaşabilirsiniz.

Page 1 of 3

Powered by WordPress & Theme by Anders Norén