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
- OOP gerçek dünyadaki varlıkları sınıf, özellik, metot ve nesne kavramlarıyla modeller.
- Bazı programlama dilleri OOP’yi destekler bazıları desteklemez.
- OOP’yi destekleyen bazı diller: C#, Java, Python, C++, Ruby, Swift, Kotlin.
- Golang OOP’nin bazı özelliklerini desteklese de genel OOP uygulama yaklaşımından uzaktır.
- OOP’yi destekleyen tüm programlama dillerinde temel yaklaşım aynı olsada farklı dillerde uygulanmasında ufak farklılıklar olabilir.
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 ile sadece field, property ve method imzalarını barındıran soyut sınıflar ya da arayüzler oluşturulur.
- Soyutlama,
abstract class
(soyut sınıflar) ya dainterface
(arayüz) kullanılarak yapılır.
Soyutlama Yöntemi: interface
interface
ile sadece özellik ve metot imzaları tanımlayabiliriz, herhangi bir kod implementasyonu içeremez.
-
Interface tanımlamaları “I” harfi ile başlar. Bu çoğu zaman zorunlu değildir ancak genel kullanım bu şekildedir.
-
Bir interface’ten türetilmiş sınıf, interface’te ki tüm metotların implementasyonunu yapmak zorundadır.
-
Soyutlamada
Interface
’ler daha yaygın olarak kullanılır.
Ö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...");
}
}
- Bir interface’i implemente eden farklı sınıflar, implemente ettikleri interface türünde kullanılabilirler.
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.