Skip to content

OOP Nedir? Temel Prensipler ve C# Uygulamaları

Yayınlandı:

Yazılım geliştirmeye yeni başlayan kişiler için nasıl büyük bir proje geliştirilebileceğini ya da var olan bir proje üzerinde, başka kısımları bozmadan, nasıl değişiklik yapabileceğini düşünmek çoğu zaman karmaşık ve korkutucu gelebilir. Bu noktada OOP’yi anlamak ve uygulayabilmek geliştiriciler için önemli hale geliyor.

OOP (Object Oriented Programming - Nesne Yönelimli Programlama), 1960’larda ortaya çıkmış ve geliştiricilerin özellikle büyük projelerde karşılaştığı zorlukları çözmeye yönelik olarak, projeleri esnek ve gelişime açık hale getirmeyi sağlayan bir programlama yaklaşımıdır. OOP’yi bir programlama yaklaşımı olarak tanımlıyoruz çünkü geliştiricilere temel prensipler sunar ve geliştiricilerin bunlara uyduğu takdirde daha organize, esnek ve gelişime açık bir proje geliştirebileceklerini söyler.

Ek Bilgiler

Temel Kavramlar

OOP’nin anlaşılmasında önemli olan Class, Method, Object, Property ve Field gibi kavramları kısaca açıklayalım.

Class (Sınıf): Gerçek dünyadaki varlıkları modelleyen bir şablondur. Bir nesnenin özelliklerinin ve davranışlarının tanımlandığı yerdir.

public class Student
{
  // Fields
  private string name;

  // Properties
  public string Name
  {
    get { return name; } // Field'ın değerini döndürür
    set { name = value; } // Field'ın değerini belirler
  }  
  public int Age {get; set;}
  public int[] Grades { get; set;}

  // Methods
  public double CalculateAverage()
  {
    int total = 0;
    foreach (int grade in Grades)
    {
      total += grade;
    }
    
    return (double)total / Grades.Length;
  }

  public void Introduce()
  {
      Console.WriteLine($"Merhaba, ben {Name}, {Age} yaşındayım.");
  }
}

Method (Metot): Bir sınıfın veya nesnenin davranışlarını tanımlayan fonksiyonlardır.

Property (Özellik): Bir sınıfın içindeki veriye dışarıdan kontrollü bir şekilde erişilmesini sağlayan yapılardır. Field’lar ile birlikte ya da tek başlarına kullanılabilirler.

Field (Alan): Bir sınıfın içindeki veri alanıdır. Genellikle private olarak tanımlanırlar.

Object (Nesne): Bir sınıfın somut bir örneğidir. Bir nesne ait olduğu sınıfın özelliklerini ve davranışlarını taşır.

// Student sınıfından bir nesne oluşturuyoruz
Student student1 = new Student();

// Property'ler aracılığıyla öğrenci bilgilerini tanımlıyoruz
student1.Name = "Ali Yılmaz";
student1.Age = 20;
student1.Grades = new int[] { 85, 90, 78 };

// Method'ları kullanarak davranışları çalıştırıyoruz
student1.Introduce();
Console.WriteLine($"Not ortalamam: {student1.CalculateAverage()}");

OOP’nin Temel Prensipleri

OOP’nin 4 temel prensibi Encapsulation, Inheritance, Abstraction ve Polymorphism.

Encapsulation (Kapsülleme)

Kapsülleme, bir sınıfın içindeki verilere erişimi, bir erişim belirteci (public, private, protected vb.) ile sınırlandırıp, property ve method’lar ile dışarıdan kontrollü bir şekilde erişilmesini sağlar.

Genelde, field’lar private olarak tanımlanır ve property’ler aracılığıyla erişilir ya da tamamen dışarıdan erişime kapalı ve sadece sınıf içinde kullanılan field’lar da olabilir.

Encapsulation prensibi, kodun daha güvenli, düzenli ve sürdürülebilir olmasını sağlar.

Örnek

Aşağıdaki örnekte bir kullanıcının banka hesabındaki bakiye doğrudan değiştirilememektedir, para yatırıldığı ya da çekildiği durumlarda bakiye değiştirilebilirdir.

public class BankAccount
{
    // Fields
    // Bakiye private olarak tanımlanmıştır, 
    // sınıf dışından doğrudan erişilemez.
    private decimal balance;

    // Properties
    // Bakiyeyi sadece okunabilir hale getiriyoruz (get), 
    // dışarıdan değiştirilmesini engelliyoruz (set yok).
    public decimal Balance
    {
        get { return balance; }
    }

    // Method: Para yatırma işlemi
    public void Deposit(decimal amount)
    {
        balance += amount;
        Console.WriteLine($"Hesaba {amount} TL yatırıldı.");
    }

    // Method: Para çekme işlemi
    public void Withdraw(decimal amount)
    {
      balance -= amount;
      Console.WriteLine($"Hesaptan {amount} TL çekildi.");
    }
}

Inheritance (Kalıtım)

Kalıtım, bir sınıfın başka bir sınıfın davranışlarını miras alabilmesini sağlar.

Bu prensip gereği, genel özellikler temel sınıfta tanımlanır ve spesifik özellikler türetilmiş sınıflarda tanımlanır.

Base Class (Temel Sınıf): Miras alınan sınıf, base class olarak ifade edilir.

Derived Class (Türetilmiş Sınıf): Miras alarak oluşturulmuş sınıf, derived class olarak ifade edilir.

Inheritance prensibi, kod tekrarını azaltır ve daha organize bir yapı oluşturulmasını sağlar.

Örnek

Aşağıdaki örnekte hayvanların ortak özellik ve davranışlarını temel sınıfta tanımladık. Türteilmiş sınıflarda da spesifik davranışlar tanımladık.

// Base Class
public class Animal
{
  // Ortak özellik: Hayvan adı
  public string Name { get; set; }

  // Ortak davranış: Yemek yeme
  public void Eat()
  {
      Console.WriteLine($"{Name} yemek yiyor.");
  }
}

// Derived Class
public class Cat : Animal
{
  // Kediye özgü bir davranış
  public void Meow()
  {
      Console.WriteLine($"{Name} miyavlıyor!");
  }
}

// Derived Class
public class Dog : Animal
{
  // Köpeğe özgü bir davranış
  public void Bark()
  {
      Console.WriteLine($"{Name} havlıyor!");
  }
}
Cat myCat = new Cat
{
    Name = "Pisi"
};
myCat.Eat();  // Temel sınıfın ortak davranışı
myCat.Meow(); // Kediye özgü davranış

Dog myDog = new Dog
{
    Name = "Karabaş"
};
myDog.Eat();  // Temel sınıfın ortak davranışı
myDog.Bark(); // Köpeğe özgü davranış

Abstraction (Soyutlama)

Abstraction prensibi, bir sınıfın sadece önemli detaylarını ortaya çıkararak karmaşıklığını gizlemeyi amaçlar. Bu sayede gereksiz detaylara dalmadan sadece sınıfın sunduğu özellik ve davranışları görebiliriz.

Soyutlama Yöntemi: interface

interface ile sadece özellik ve metot imzaları tanımlayabiliriz, herhangi bir kod implementasyonu içeremez.

Örnek

Car ve Truck sınıflarının içerisine bakmadan sadece IVehicle arayüzüne bakarak bu sınıfların hangi metotları barındırdığını görebiliriz.

// Interface tanımlaması
public interface IVehicle
{
    void Start();  // Araç başlatma
    void Stop();   // Araç durdurma
}

// Sınıf implementasyonu
public class Car : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Araba çalıştırılıyor...");
    }

    public void Stop()
    {
        Console.WriteLine("Araba durduruluyor...");
    }
}

// Sınıf implementasyonu
public class Truck : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Kamyon çalıştırılıyor...");
    }

    public void Stop()
    {
        Console.WriteLine("Kamyon durduruluyor...");
    }
}
IVehicle car = new Car();
car.Start();
car.Stop();

IVehicle truck = new Truck();
truck.Start();
truck.Stop();

Bu örnekte, Car ve Truck sınıfları, IVehicle arayüzünü implemente ettiği için, her ikisi de IVehicle türünde referans alabilir. Böylece, kodumuzun bağımlılıklarını azaltmış ve esnekliği artırmış oluruz.

Soyutlama Yöntemi: abstract class

abstract class ile hem metot imzaları barındırabiliriz hem de ortak kullanım için implementasyonu olan metotlar tanımlayabiliriz.

Örnek

Aşağıdaki örnekte Shape soyut sınıfı içerisinde implementasyonu olan DisplayArea metodu tanımladık. Bu metodu türetilmiş sınıfta tekrardan tanımlamadan direkt kullanabiliriz. Ancak implementasyonu bulunmayan, sadece imzası bulunan GetArea metodunun türetilmiş sınıf içerisinde implementasyonu yapılmış olması gerekir.

// Soyut sınıf
public abstract class Shape
{
    // Soyut metot (implementasyonu yok)
    public abstract double GetArea();

    // Normal metot
    public void DisplayArea()
    {
        Console.WriteLine($"Area: {GetArea()}");
    }
}

// Soyut sınıftan türemiş sınıf
public class Circle : Shape
{
    public double Radius { get; set; }

    public override double GetArea()
    {
        return Math.PI * Radius * Radius;
    }
}
Shape circle = new Circle();
circle.Radius = 5;
circle.DisplayArea();
circle.GetArea();

Polymorphism (Çok Biçimlilik)

Polymorphism prensibi, aynı isimdeki metotların farklı sınıflarda farklı şekillerde davranmasını sağlar. Bu sayede, aynı interface veya temel sınıftan üretilmiş farklı nesnelerle çalışılabilir.

Polymorphism, overloading ve overriding ile sağlanır.

Overloading (Aşırı Yükleme)

Aynı isimli metodun farklı sayıda ve türde parametrelerle farklı şekillerde davranması sağlanır.

Aşağıdaki örnekte 3 farklı metot aynı isimle tanımlanmıştır. Hangi metodun çalışacağı, metoda gönderilen parametre sayısı ve türüne göre otomatik olarak belirlenir.

public class Calculator
{
    // 1. Metot: İki tam sayıyı toplar
    public int Add(int a, int b)
    {
        return a + b;
    }

    // 2. Metot: Üç tam sayıyı toplar
    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }

    // 3. Metot: İki ondalıklı sayıyı toplar
    public double Add(double a, double b)
    {
        return a + b;
    }
}
Calculator calculator = new Calculator();

// Farklı Add metotlarını çağırıyoruz
Console.WriteLine(calculator.Add(2, 3));          // Output: 5
Console.WriteLine(calculator.Add(2, 3, 4));       // Output: 9
Console.WriteLine(calculator.Add(2.5, 3.5));      // Output: 6.0

Overriding (Ezme)

Temel sınıftan türetilmiş bir sınıf içerisinde, temel sınıftan miras alınan metotların aynı isimle yeniden tanımlamasını yapmak için kullanılır.

// Base Class
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Hayvan bir ses çıkarıyor.");
    }
}

// Derived Class: Cat
public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Kedi miyavlıyor.");
    }
}

// Derived Class: Dog
public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Köpek havlıyor.");
    }
}
Animal genericAnimal = new Animal();
genericAnimal.MakeSound(); // Output: Hayvan bir ses çıkarıyor.

Animal myCat = new Cat();
myCat.MakeSound();         // Output: Kedi miyavlıyor.

Animal myDog = new Dog();
myDog.MakeSound();         // Output: Köpek havlıyor.

Sonuç

OOP, birçok programlama dilinin desteklediği, temel prensipleri bulunan, tüm varlıkların sınıflar ve nesneler ile modellendiği, karmaşık sistemlerin daha düzenli ve sürdürülebilir bir şekilde geliştirilebilmesini sağlayan bir programlama yaklaşımıdır.

Ayrıca geliştiricilerin OOP’yi doğru uygulayabilmesi için SOLID prensipleri geliştirilmiştir.

SOLID prensipleri hakkındaki yazımı okumak için tıklayınız.


Önceki Yazı
SOLID Prensipleri: Kod Yazmanın SOLID Yolu