C# Nesne Yönelimli Programlamayı Anlamak
Kalıtım ve arayüzler, nesne tabanlı programlamanın (OOP) ayrılmaz parçalarıdır. Tim Corey, "C#'da Kalıtım vs Arayüzler: Nesne Tabanlı Programlama" başlıklı videosunda, kalıtımı ne zaman kullanmanız gerektiği ve arayüzleri ne zaman tercih etmeniz gerektiği konusunda detaylı bir açıklama sunar.
Bu makale, Tim Corey'nin videosunun kapsamlı bir rehberi olarak hizmet eder. Anahtar kavramlar, örnekler ve videoda sunulan kod açıklamalarını parçalayarak kalıtım ile arayüzler arasındaki farklılıkları vurgular ve her birinin ne zaman kullanılacağını açıklar.
Giriş
Tim, (0:00)'da kalıtım ve arayüzler arasındaki farkı ayırt etmenin önemini vurgulayarak başlar. Her kavramı ne zaman kullanılacağını anlamanın en iyi sonuçları elde etmenin gerekliliğini vurgular. Hedefi, tekli kalıtımın yanlış kullanımının örneklerinden başlayarak, bunları düzeltmeye giderek örneklerle göstermektir.
Proje Oluşturma
1:08'de Tim, .NET 5 kullanarak basit bir konsol uygulaması oluşturur. Projeye "OODemoApp" adını verir ve ana hedefin üretim için hazır kod oluşturmaktan ziyade kavramları göstermek olduğunu açıklar.
Kalıtımı Anlamak
Tim, 1:55'te kalıtımın temellerine iner. Kalıtımı, bir temel sınıfın özelliklerinin ve yöntemlerinin bir türemiş sınıf tarafından miras alındığı bir mekanizma olarak tanımlar. Kalıtımın sadece kodun tekrar kullanımını ve paylaşımını sağlamak için değil, mantıksal bir 'is-a' ilişkisini kurmak için kullanılmaması gerektiğini vurgular.
Anahtar noktalar:
- Is-a İlişkisi: Türemiş sınıfın temel sınıfın bir türü olduğundan emin olmak.
- Ortak Mantık: Miras alınan sınıfların sadece nesne ya da yöntem imzası değil, ortak mantığı paylaşmaları.
Örnek Sınıf: Kiralık Araba
Tim, 7:52'de kalıtımın temel kavramını göstermek için RentalCar sınıfını oluşturur. Bu sınıf, Miami, Florida'daki bir kiralama ajansındaki bir kiralık arabayı temsil eder.
public class RentalCar
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public int NumberOfPassengers { get; set; }
public void StartEngine()
{
Console.WriteLine("Turn key to ignition setting");
Console.WriteLine("Turn key to on");
}
public void StopEngine()
{
Console.WriteLine("Turn key to off");
}
}public class RentalCar
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public int NumberOfPassengers { get; set; }
public void StartEngine()
{
Console.WriteLine("Turn key to ignition setting");
Console.WriteLine("Turn key to on");
}
public void StopEngine()
{
Console.WriteLine("Turn key to off");
}
}Yanlış Kalıtım Kullanımının Tehlikelerini Gösterme
Tim, 10:15'te kalıtımın yanlış kullanılması durumunda nasıl sorunlara yol açabileceğini açıklar. Kalıtımın doğru kullanılmaması durumunda, yönetilmesi ve genişletilmesi zor kodlara yol açabileceğine dikkat çeker. Kalıtımı sadece kod paylaşımı için kullanmaktan kaçınmayı tavsiye eder.
Araç ve Kamyon Türleri için Enums Ekleme
Tim, 10:45'te araç türleri için bir enum ekler. Tüm enumları tek bir yerde tutmak için Enums.cs adlı yeni bir sınıf dosyası oluşturur. Bu enum, farklı araç stillerini ayırt etmeye yardımcı olacaktır.
// Enums.cs
public enum CarType
{
Hatchback,
Sedan,
Compact
}// Enums.cs
public enum CarType
{
Hatchback,
Sedan,
Compact
}Ardından, RentalCar sınıfına araç türünü belirtmek için bir özellik ekler.
public class RentalCar : RentalVehicle
{
public CarType Style { get; set; }
// Other properties and methods
}public class RentalCar : RentalVehicle
{
public CarType Style { get; set; }
// Other properties and methods
}Kamyonları Dahil Etmek İçin Genişletme
Tim, 12:27'de açıkladığı üzere, kiralama ajansı filosuna kamyonları ekleme kararı alır, bu da yeni gereksinimleri tanıtır. Ebeveyn sınıf RentalVehicle'dan türeyen bir RentalTruck sınıfı oluşturur.
public class RentalTruck : RentalVehicle
{
public TruckType Style { get; set; }
// Other properties and methods
}public class RentalTruck : RentalVehicle
{
public TruckType Style { get; set; }
// Other properties and methods
}Ardından kamyon türleri için yeni bir enum tanımlar.
// Enums.cs
public enum TruckType
{
ShortBed,
LongBed
}// Enums.cs
public enum TruckType
{
ShortBed,
LongBed
}Farklı Özellik Türlerini Yönetme
Tim, 15:28'de iki özelliğin aynı adı taşımasının onların aynı olduğu anlamına gelmediğini vurgular. Bunu, arabalar için CarType ve kamyonlar için TruckType olarak farklı enumları temsil edebilecek olan Style özelliği ile açıklar.
Filo İçin Tekneleri Dahil Etme
Kiralama ajansı filosunu tekneleri içerecek şekilde genişletir. Tim, bir RentalBoat sınıfı oluşturarak bununla başa çıkma yolunu gösterir. Başlangıçta, teknelerin arabalar ve kamyonlarla bazı özellikleri paylaşabileceği için yönetilebilir görünüyor.
public class RentalBoat : RentalVehicle
{
// Properties and methods specific to boats
}public class RentalBoat : RentalVehicle
{
// Properties and methods specific to boats
}Yelkenlilerle Baş Etme
Yelkenlilerin tanıtımı, yelkenlilerin motoru olmadığı için bazı zorluklar sunar. Tim, 19:57'de bu senaryoda kalıtımın sınırlamalarını açıklar.
public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}Arayüzlerle Sorunu Çözmek
Tim, StartEngine ve StopEngine metodlarını türetilmiş sınıflarda geçersiz kılınmalarına izin verecek şekilde temel sınıfta sanal yapmayı öneriyor.
public abstract class RentalVehicle
{
// Common properties
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}public abstract class RentalVehicle
{
// Common properties
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}Çağrılması Gerekmeyen Yöntemleri Ele Alma
Tim, miras alınan sınıflarda çağrılması gerekmeyen yöntemlerin tuzaklarını anlatıyor (21:56). Motoru olmayan RentalSailboat sınıfı örneği için, RentalVehicle sınıfından StartEngine ve StopEngine yöntemlerini devralıyor. Bu yöntemler yanlışlıkla çağrıldığında, uygun olmadıklarını göstermek için istisna atmak zorunda kaldıklarından sorunlara yol açabilir.
public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}Mirasın Sınırlarını Tanıma
Tim, mirasın mantıklı olmadığında karmaşık ve değişken bir kod tabanı oluşturabileceğini vurguluyor (24:06). Örneğin, bir yelkenli bir motorlu RentalVehicle olarak ele alınmamalıdır. Bu, mirasın sınırlamalarını ve daha iyi bir tasarım yaklaşımına olan ihtiyaçı gösterir.
Arayüzlerle Daha İyi Bir Tasarıma Geçiş
Bu sorunları ele almak için, Tim arayüzler kullanarak daha iyi bir tasarım öneriyor. Önce 'BetterOODemo' adında yeni bir konsol uygulaması projesi oluşturuyor ve gelişmiş yaklaşımı gösteriyor.
Arayüzü Tanımlama
Tim, tüm kiralamalara ortak özellikleri kapsayacak şekilde IRental arayüzünü tanıtıyor.
public interface IRental
{
int RentalId { get; set; }
string CurrentRenter { get; set; }
decimal PricePerDay { get; set; }
}public interface IRental
{
int RentalId { get; set; }
string CurrentRenter { get; set; }
decimal PricePerDay { get; set; }
}Karada Gidecek Araçlar için Temel Sınıf Oluşturma
Tim ardından kara araçları için bir temel sınıf oluşturuyor, araç kiralama kavramını araçtan ayırıyor.
public abstract class LandVehicle
{
public int NumberOfPassengers { get; set; }
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}public abstract class LandVehicle
{
public int NumberOfPassengers { get; set; }
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}Temel araç sınıfını LandVehicle olarak yeniden adlandırarak, sadece uygun araçların motorla ilgili yöntemleri devralmasını sağlar.
Araba ve Kamyon Sınıflarını Uygulama
Tim, LandVehicle'dan türeyen ve IRental arayüzünü uygulayan Araba ve Kamyon sınıfları oluşturur.
public class Car : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public CarType Style { get; set; }
}
public class Truck : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public TruckType Style { get; set; }
}public class Car : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public CarType Style { get; set; }
}
public class Truck : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public TruckType Style { get; set; }
}Bu tasarım, her sınıfın yalnızca kendi türüne uygun olan özellikleri ve yöntemleri içermesini sağlayarak endişelerin net bir şekilde ayrılmasını sağlar.
Kod Tekrarı Yapmaktan Kaçınma
Tim, gereksiz kod tekrarı yapmamanın önemini tartışıyor (31:41). IRental arayüzü, birden çok sınıfta aynı özellikleri gerektirirken, bu DRY (Don't Repeat Yourself) ilkesinin ihlali olarak kabul edilmez çünkü kopyalanan bir mantık yoktur—sadece özellik bildirimi.
Kiralanabilir Yelkenli Sınıfını Uygulama
Tim, Kiralanabilir Yelkenli sınıfını LandVehicle'dan miras almak yerine IRental arayüzünü uygulayarak ayrı bir şekilde ele almayı açıklıyor (35:09). Bu yaklaşım, uygunsuz miras ile ilgili tuzaklardan kaçınmaya yardımcı olur.
public class Sailboat : IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
// Additional properties and methods specific to sailboats
}public class Sailboat : IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
// Additional properties and methods specific to sailboats
}Farklı Kiralamaları Yönetme
Tim, farklı türde kiralamaları yönetmek için bir liste kurar, IRental arayüzünü kullanarak kamyonlar, yelkenliler ve arabalar dahil olmak üzere çeşitli kiralama türlerini depolar.
List<IRental> rentals = new List<IRental>
{
new Truck { CurrentRenter = "Truck Renter" },
new Sailboat { CurrentRenter = "Sailboat Renter" },
new Car { CurrentRenter = "Car Renter" }
};List<IRental> rentals = new List<IRental>
{
new Truck { CurrentRenter = "Truck Renter" },
new Sailboat { CurrentRenter = "Sailboat Renter" },
new Car { CurrentRenter = "Car Renter" }
};Bu tasarım, kiralamalar arasında döngü yaparak CurrentRenter, PricePerDay ve RentalId gibi ortak özelliklere erişime olanak tanır.
foreach (var rental in rentals)
{
Console.WriteLine($"Renter: {rental.CurrentRenter}, Price Per Day: {rental.PricePerDay}");
}foreach (var rental in rentals)
{
Console.WriteLine($"Renter: {rental.CurrentRenter}, Price Per Day: {rental.PricePerDay}");
}Belirli Türlere Dönüştürme
Farklı kiralama türlerinin belirli özelliklerine ve yöntemlerine erişmek için, Tim, nesneleri kendi türlerine kontrol etmek ve dönüştürmek amacıyla is anahtar kelimesini nasıl kullanacağını göstermektedir.
foreach (var rental in rentals)
{
if (rental is Truck truck)
{
Console.WriteLine($"Truck Style: {truck.Style}, Passengers: {truck.NumberOfPassengers}");
}
else if (rental is Sailboat sailboat)
{
// Access sailboat-specific properties
}
else if (rental is Car car)
{
// Access car-specific properties
}
}foreach (var rental in rentals)
{
if (rental is Truck truck)
{
Console.WriteLine($"Truck Style: {truck.Style}, Passengers: {truck.NumberOfPassengers}");
}
else if (rental is Sailboat sailboat)
{
// Access sailboat-specific properties
}
else if (rental is Car car)
{
// Access car-specific properties
}
}Arayüzlerle Esneklik
Tim, arayüzleri kullanmanın esnekliğinin gelecekteki değişikliklere imkân sağladığını vurguluyor. Örneğin, tanklar veya televizyonlar gibi yeni kiralama türleri eklemek mevcut yapıyı bozmaz.
Mirasin Aşırı Kullanımından Kaçınma
Tim, mirasın kod paylaşımı için aşırı kullanılmaması gerektiği konusunda uyarıyor, çünkü bu karmaşık ve esnek olmayan bir kod tabanına yol açabilir. Bunun yerine, anlamın ötesine geçmeden 'is-a' ilişkisinin sınırının ötesine geçmeye gerek kalmadan sonuçlar elde etmek için arayüzler ve kompozisyonlardan yararlanmalarını öneriyor.
Sonuç
Tim Corey'nin OOP'de miras ve arayüzlerin açıklaması, sürdürülebilir ve esnek bir kod oluşturma yoluna açık bir yol sunuyor. Yaygın tuzakları göstererek ve arayüzlerle incelikli bir tasarım sağlayarak, geliştiricilerin uygulamalarını etkili bir şekilde yapılandırma konusunda bilgi sahibi kararlar verip yapmasını sağlıyor.
Bu kavramları daha yakından incelemek ve yürürlükte görmek için, Tim'in tamamlanmış videosunu izleyin. Onun kanalı, programlama eğitimlerinin bir hazinesi. Kaçırmayın!

