
SOLID NEDİR?
SOLID, yazılım mühendisliği prensiplerini ifade eden bir kısaltmadır. Bu prensipler, yazılım geliştirme sürecinde kodun daha okunabilir, sürdürülebilir ve genişletilebilir olmasını sağlamayı hedefler. SOLID prensipleri, yazılım mühendislerine tasarım kalıplarını uygulamada rehberlik eder ve yazılımın değişikliklere uyum sağlamasını kolaylaştırır.
SOLID, beş farklı prensibi içerir. Bu prensipler tek tek incelenerek, her biri için C# örnekleriyle açıklanabilir:
Single Responsibility Principle (SRP) - Tek Sorumluluk Prensibi: SRP, her sınıfın yalnızca bir tek sorumluluğu olması gerektiğini belirtir. Bir sınıfın birden fazla sorumluluğu olduğunda, her bir sorumluluğun ayrı ayrı bir sınıfa ait olması daha uygun olacaktır. Aşağıda bir örnek verilmiştir:
// Kötü örnek - Birden fazla sorumluluk içeren sınıf
public class Customer
{
public void AddCustomer()
{
// Müşteri ekleme işlemleri
}
public void GenerateInvoice()
{
// Fatura oluşturma işlemleri
}
}
// İyi örnek - Her sınıf tek bir sorumluluğa sahip
public class CustomerManager
{
public void AddCustomer()
{
// Müşteri ekleme işlemleri
}
}
public class InvoiceManager
{
public void GenerateInvoice()
{
// Fatura oluşturma işlemleri
}
}
Open/Closed Principle (OCP) - Açık/Kapalı Prensibi: OCP, mevcut kodun değişikliklere kapalı (closed) olmasını, ancak yeni davranışların eklenmesine açık (open) olmasını savunur. Bu prensip, kodun genişletilebilirliğini artırmayı hedefler. Aşağıda bir örnek verilmiştir:
// Kötü örnek - Yeni durum eklendiğinde sınıf değiştirilmelidir
public class Order
{
public void CalculateTotal()
{
// Sipariş toplamını hesapla
}
}
// İyi örnek - Yeni durumları desteklemek için sınıfı genişlet
public abstract class Order
{
public abstract void CalculateTotal();
}
public class StandardOrder : Order
{
public override void CalculateTotal()
{
// Standart sipariş toplamını hesapla
}
}
public class DiscountedOrder : Order
{
public override void CalculateTotal()
{
// İndirimli sipariş toplamını hesapla
}
}
Liskov Substitution Principle (LSP) - Liskov Yerine Geçme Prensibi: LSP, bir sınıfın, onu temel alan tüm alt sınıflar tarafından yerine geçilebilir olması gerektiğini ifade eder. Bu prensip, alt sınıfların üst sınıfların davranışını değiştirmeden kullanılabilmesini sağlar. İşte bir örnek:
// Kötü örnek - Liskov Yerine Geçme Prensibine uyulmamış
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public int CalculateArea()
{
return Width * Height;
}
}
public class Square : Rectangle
{
public override int Width
{
get { return base.Width; }
set
{
base.Width = value;
base.Height = value;
}
}
public override int Height
{
get { return base.Height; }
set
{
base.Width = value;
base.Height = value;
}
}
}
// İyi örnek - Liskov Yerine Geçme Prensibine uyulmuş
public abstract class Shape
{
public abstract int CalculateArea();
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public override int CalculateArea()
{
return Width * Height;
}
}
public class Square : Shape
{
public int SideLength { get; set; }
public override int CalculateArea()
{
return SideLength * SideLength;
}
}
Interface Segregation Principle (ISP) - Arayüz Ayırma Prensibi: ISP, mümkün olduğunda ihtiyaçlara özel arayüzler sağlamanın daha iyi olduğunu belirtir. Bir sınıfın kullanmadığı arayüz metotlarına bağımlı olmaması gerektiğini ifade eder. İşte bir örnek:
// Kötü örnek - Arayüzün kullanılmayan metotlarına bağımlılık
public interface IShape
{
void Draw();
void Rotate();
void Resize();
}
public class Circle : IShape
{
public void Draw()
{
// Çemberi çiz
}
public void Rotate()
{
// Çemberi döndür
}
public void Resize()
{
// Çemberi yeniden boyutlandır
}
}
// İyi örnek - Arayüzlerin daha küçük parçalara ayrılması
public interface IDrawable
{
void Draw();
}
public interface IRotatable
{
void Rotate();
}
public interface IResizable
{
void Resize();
}
public class Circle : IDrawable
{
public void Draw()
{
// Çemberi çiz
}
}
public class Square : IDrawable, IRotatable, IResizable
{
public void Draw()
{
// Kareyi çiz
}
public void Rotate()
{
// Kareyi döndür
}
public void Resize()
{
// Kareyi yeniden boyutlandır
}
}
C# dilinde bir arayüz (interface) kullanarak IF bağımlılığından kurtulmayı gösteren bir örnek:
using System;
// IF bağımlılığından kurtulmak için bir arayüz (interface) tanımlayalım.
public interface ILogger
{
void Log(string message);
}
// ILogger arayüzünü uygulayan bir sınıf örneği oluşturalım.
public class FileLogger : ILogger
{
public void Log(string message)
{
// Mesajı bir dosyaya kaydetme işlemini burada gerçekleştirin.
Console.WriteLine("Dosyaya loglandı: " + message);
}
}
// ILogger arayüzünü uygulayan başka bir sınıf örneği oluşturalım.
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
// Mesajı konsola yazdırma işlemini burada gerçekleştirin.
Console.WriteLine("Konsola loglandı: " + message);
}
}
// ILogger arayüzünden bağımsız olarak çalışacak bir sınıf tanımlayalım.
public class UserManager
{
private ILogger logger;
// Bağımlılığı enjekte etmek için bir constructor tanımlayalım.
public UserManager(ILogger logger)
{
this.logger = logger;
}
public void AddUser(string username)
{
// Kullanıcı ekleme işlemini burada gerçekleştirin.
// Loglama işlemi IF bağımlılığından kurtulacak şekilde arayüz üzerinden yapılır.
logger.Log("Kullanıcı eklendi: " + username);
}
}
public class Program
{
public static void Main(string[] args)
{
// Dosya loglayıcıyı kullanarak UserManager sınıfını örnekleyelim.
ILogger fileLogger = new FileLogger();
UserManager userManager1 = new UserManager(fileLogger);
userManager1.AddUser("John Doe");
// Konsol loglayıcıyı kullanarak UserManager sınıfını örnekleyelim.
ILogger consoleLogger = new ConsoleLogger();
UserManager userManager2 = new UserManager(consoleLogger);
userManager2.AddUser("Jane Smith");
Console.ReadLine();
}
}
Yukarıdaki örnekte, ILogger
arayüzü loglama işlevini tanımlar. Ardından FileLogger
ve ConsoleLogger
sınıfları, ILogger
arayüzünü uygular ve loglama işlevini kendi yöntemlerinde gerçekleştirir.
UserManager
sınıfı, loglama işlevini kullanmak için ILogger
arayüzünden bağımlıdır. Ancak, arayüz üzerinden bağımlılığı enjekte ederek (dependency injection) IF bağımlılığından kurtulur. UserManager
sınıfının yapıcı (constructor) yöntemi, bir ILogger
örneği alır ve bu örneği kullanarak loglama işlemini gerçekleştirir.
Main metodu içerisinde, FileLogger
ve ConsoleLogger
örnekleri oluşturulup, bunlar UserManager
sınıfına enjekte edilerek kullanılır. Buşekilde UserManager
sınıfı, loglama işlemini hangi logger'ın kullanılacağına bağımlı olmadan çalışabilir hale gelir. Bu, IF bağımlılığını azaltır ve kodun daha esnek ve genişletilebilir olmasını sağlar.
Dependency Inversion Principle (DIP) - Bağımlılıkların Ters Çevrilmesi Prensibi: DIP, bir sınıfın, daha düşük seviyeli ayrıntılara doğrudan bağımlı olmaması ve soyutlamalara (interface, abstract class) bağımlı olması gerektiğini ifade eder. Bu prensip, sınıflar arasındaki bağımlılıkları gevşeterek, kodun daha esnek ve değişime açık olmasını sağlar. İşte bir örnek:
// Kötü örnek - Düşük seviyeli bağımlılık
public class ProductService
{
private SqlDatabase _database;
public ProductService()
{
_database = new SqlDatabase();
}
public void AddProduct(Product product)
{
_database.Save(product);
}
}
// İyi örnek - Yüksek seviyeli soyutlamaya bağımlılık
public class ProductService
{
private IDatabase _database;
public ProductService(IDatabase database)
{
_database = database;
}
public void AddProduct(Product product)
{
_database.Save(product);
}
}
public interface IDatabase
{
void Save(Product product);
}
public class SqlDatabase : IDatabase
{
public void Save(Product product)
{
// Ürünü SQL veritabanına kaydet
}
}
public class InMemoryDatabase : IDatabase
{
public void Save(Product product)
{
// Ürünü bellekte tutulan veritabanına kaydet
}
}
Inversion of Control (IoC) ve Dependency Inversion Principle (DIP) arasında yakın bir ilişki vardır. İkisi de yazılım tasarımı ve geliştirme sürecinde bağımlılıkların yönetimi ve kodun daha esnek hale getirilmesi için önemli prensipleri ifade eder.
Dependency Inversion Principle (DIP), bileşenler arasındaki bağımlılıkları yönetmek için soyutlamalara (interface, abstract class) dayanır. DIP, üst seviye modüllerin alt seviye modüllere doğrudan bağımlı olmamasını, ancak soyutlamalara bağımlı olmasını savunur. Bu, yüksek seviye modüllerin düşük seviye modüllerin ayrıntılarına bağımlı olmadan çalışabilmesini sağlar.
IoC ise, DIP prensibinin uygulanmasını destekleyen bir tasarım prensibidir. IoC, bağımlılıkların bileşenlere dışarıdan enjekte edildiği bir yaklaşımı ifade eder. Bileşenler, bağımlılıklarını kendileri oluşturup yönetmek yerine, dışarıdan alır. Bu, bileşenlerin daha gevşek bağlı, değiştirilebilir ve test edilebilir olmasını sağlar.
IoC, DIP prensibini uygulamanın bir yöntemidir. IoC, Dependency Injection (Bağımlılık Enjeksiyonu) kullanılarak gerçekleştirilebilir. Dependency Injection, IoC prensibini uygulamanın bir yolu olarak düşünülebilir. Dependency Injection, bağımlılıkların bileşenlere dışarıdan enjekte edildiği bir tekniktir ve bu sayede DIP prensibi uygulanır.
Dolayısıyla, IoC ve DIP birbirini tamamlayan prensiplerdir. IoC, bileşenlerin bağımlılıklarını dışarıdan almasını sağlayarak DIP prensibini destekler. DIP ise, bileşenler arasındaki bağımlılıkları soyutlamalar üzerinden yöneterek IoC'nin uygulanmasını kolaylaştırır. Bu iki prensip birlikte kullanıldığında, daha esnek, sürdürülebilir ve test edilebilir bir yazılım tasarımı elde edilebilir.
Inversion of Control (IoC), bir yazılım bileşeninin, kendi bağımlılıklarını oluşturup yönetmek yerine, bu bağımlılıkları dışarıdan almasını sağlayan bir tasarım prensibidir. IoC, bağımlılıkları tersine çevirerek, bir bileşene neyin sağlanacağını kontrol etmek yerine, bileşenin ihtiyaç duyduğu kaynakları dışarıdan almasını sağlar.
IoC'nin temel amacı, bir bileşenin daha esnek, gevşek bağlı ve test edilebilir olmasını sağlamaktır. Bu prensip, bağımlılıkların sıkı bir şekilde bileşene bağlı olmasını engeller ve yerine bileşenin bağımlılıklarını alabileceği bir arayüz veya soyutlama kullanır. Bu, bileşenin uygulama mantığından ayrılmış ve değiştirilebilir hale gelmesini sağlar.
IoC, genellikle bir Inversion of Control Container (IoC Container) kullanılarak uygulanır. Bu konteynerler, bağımlılıkları otomatik olarak oluşturmayı, yönetmeyi ve çözmeyi sağlar. IoC Container, bileşenler arasındaki bağımlılıkları yönetir ve bileşenleri ihtiyaç duydukları bağımlılıklarla bir araya getirir.
IoC'nin faydaları şunlardır:
-
Gevşek bağlılık: Bileşenlerin belirli bir uygulama veya hizmete bağlı olmamasını sağlar. Bu, bileşenlerin yeniden kullanılabilirliğini artırır ve bağımlılıkların daha kolay değiştirilebilmesini sağlar.
-
Test edilebilirlik: Bağımlılıkları dışarıdan almak, bileşenlerin izole edilmiş bir şekilde test edilebilmesini sağlar. Bağımlılıkları taklit eden veya sahte nesnelerle değiştirerek bileşenleri kolayca test edebilirsiniz.
-
Modülerlik: Bileşenler arasındaki bağımlılıkları yönetmek ve bileşenlerin bir araya getirilmesini kolaylaştırmak, uygulamanın modülerliğini artırır. Bileşenlerin kolayca değiştirilebilmesi ve yeniden kullanılabilmesi sağlanır.
-
Değiştirilebilirlik: Bağımlılıkları dışarıdan almak, uygulama gereksinimlerinde veya yeni özellikler eklediğinizde bileşenleri daha kolay değiştirebilmenizi sağlar. Bu, uygulamanın daha esnek ve genişletilebilir olmasını sağlar.
IoC, genellikle Dependency Injection (Bağımlılık Enjeksiyonu) olarak da anılır. Dependency Injection, IoC prensibini uygulamanın bir yöntemidir ve IoCContainer'ın kullanıldığı bir enjeksiyon tekniğidir. Bu teknik, bileşenin bağımlılıklarının otomatik olarak enjekte edildiği bir yöntemdir.
Dependency Injection (Bağımlılık Enjeksiyonu), IoC prensibini uygulamanın bir yöntemidir ve IoC Container'ın kullanıldığı bir enjeksiyon tekniğidir. Bu teknik, bileşenin bağımlılıklarının otomatik olarak enjekte edildiği bir yöntemdir.
Dependency Injection (Bağımlılık Enjeksiyonu) örneği C# dilinde aşağıdaki gibi gösterilebilir:
public class Logger
{
public void Log(string message)
{
// Loglama işlemleri
}
}
public class ProductService
{
private readonly Logger _logger;
public ProductService(Logger logger)
{
_logger = logger;
}
public void AddProduct(string name)
{
// Ürün ekleme işlemleri
_logger.Log("Ürün eklendi: " + name);
}
}
public static void Main(string[] args)
{
Logger logger = new Logger();
ProductService productService = new ProductService(logger);
productService.AddProduct("Bilgisayar");
}
Yukarıdaki örnekte, ProductService
sınıfı bir Logger
nesnesine bağımlıdır. Ancak bu bağımlılık, Dependency Injection kullanılarak çözülmüştür. ProductService
'in yapıcı metodu Logger
nesnesini parametre olarak alır ve bu nesne enjekte edilir. Böylece ProductService
sınıfı, dışarıdan gelen Logger
nesnesini kullanabilir. Bu yöntem sayesinde ProductService
'in bağımlılıkları kolayca değiştirilebilir ve test edilebilir hale gelir.
Dependency Injection ile IoC birbirine sıkı sıkıya bağlıdır ve birlikte kullanılırlar. IoC, bileşenlerin bağımlılıklarını yönetme prensibini ifade ederken, Dependency Injection bu prensibi uygulamanın bir yöntemidir ve IoC Container'ın kullanıldığı bir enjeksiyon tekniğini ifade eder.
Bu örnekler, SOLID prensiplerinin C# dilinde nasıl uygulanabileceğini göstermektedir. Her prensip, kodun daha temiz, esnek ve sürdürülebilir olmasını sağlamak için önemli bir rol oynar. Bu prensipleri anlamak ve doğru bir şekilde uygulamak, yazılım projelerinin kalitesini artırır ve gelecekteki değişikliklere uyum sağlamayı kolaylaştırır.
YAGNI (You Ain't Gonna Need It)
YAGNI (You Ain't Gonna Need It), gereksiz işlevsellik veya gereksiz kod eklemekten kaçınmayı öneren bir yazılım prensibidir. Bu prensibe göre, mevcut gereksinimleri karşılamak için yazılımın minimum düzeyde olması ve gelecekte ihtiyaç duyulmayacak özelliklerin önemsenmemesi gerekmektedir. Böylece, kod karmaşıklığı azalır, sürdürülebilirlik artar ve gereksiz iş yükü engellenir.
Aşağıda, YAGNI prensibinin C# dilinde nasıl uygulanabileceğini gösteren detaylı bir örnek verilmiştir:
public class ShoppingCart
{
private List<Product> _products;
public ShoppingCart()
{
_products = new List<Product>();
}
public void AddProduct(Product product)
{
_products.Add(product);
}
public void RemoveProduct(Product product)
{
_products.Remove(product);
}
public decimal CalculateTotal()
{
decimal total = 0;
foreach (var product in _products)
{
total += product.Price;
}
return total;
}
// YAGNI prensibine göre, aşağıdaki metot gereksiz olduğu için kaldırılmıştır.
// public void PrintInvoice()
// {
// Console.WriteLine("Invoice Details:");
// foreach (var product in _products)
// {
// Console.WriteLine($"{product.Name} - {product.Price}");
// }
// Console.WriteLine($"Total: {CalculateTotal()}");
// }
}
public static void Main(string[] args)
{
ShoppingCart cart = new ShoppingCart();
Product product1 = new Product("Phone", 1000);
Product product2 = new Product("Laptop", 2000);
cart.AddProduct(product1);
cart.AddProduct(product2);
decimal total = cart.CalculateTotal();
Console.WriteLine($"Total: {total}");
// YAGNI prensibine göre, aşağıdaki metot gereksiz olduğu için kaldırılmıştır.
// cart.PrintInvoice();
}
Yukarıdaki örnekte, ShoppingCart
sınıfı bir alışveriş sepetini temsil eder. Sepete ürün eklemek, ürün çıkarmak ve toplam tutarı hesaplamak için metotları vardır.
Ancak, YAGNI prensibine göre, şu anda ihtiyaç duyulmayan bir özellik olan PrintInvoice
metodu gereksizdir ve kaldırılmalıdır. Bu metot, sepetteki ürünleri ve toplam tutarı bir fatura şeklinde yazdırmak için kullanılmıştır. Ancak, mevcut gereksinimlere göre bu özellik gereksizdir ve kodu gereksiz yere karmaşıklaştırır.
YAGNI prensibine uygun olarak, sadece mevcut gereksinimlere odaklanan ve minimum düzeyde olan fonksiyonelliği içeren bir kod tasarımı benimsenmelidir. Gereksinimler değiştikçe veya yeni gereksinimler ortaya çıktıkça kod geliştirilebilir, ancak gereksinimlerin olmadığı varsayılan özellikler eklemekten kaçınılmalıdır.
YAGNI prensibini uygulamak, yazılımın daha basit, anlaşılır, sürdürülebilir ve değişime uyumlu hale gelmesini sağlar.
KISS (Keep It Simple, Stupid)
KISS prensibi, yazılımın basit ve anlaşılır olmasını sağlar. Karmaşık yapılar yerine, basit ve doğrudan anlaşılabilir kod yapısı tercih edilir. Bu sayede, kodun sürdürülebilirliği artar, hatalar daha kolay tespit edilir ve kodun genel kalitesi yükselir.
KISS prensibi, gereksiz karmaşıklığı önler, geliştirme sürecini kolaylaştırır ve yazılım projelerinin başarıyla tamamlanmasını sağlar.
KISS (Keep It Simple, Stupid) prensibinin C# dilinde detaylı bir örneği:
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Subtract(int a, int b)
{
return a - b;
}
public int Multiply(int a, int b)
{
return a * b;
}
public int Divide(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException("Cannot divide by zero.");
}
return a / b;
}
}
public static void Main(string[] args)
{
Calculator calculator = new Calculator();
int sum = calculator.Add(5, 3);
Console.WriteLine($"Sum: {sum}");
int difference = calculator.Subtract(10, 4);
Console.WriteLine($"Difference: {difference}");
int product = calculator.Multiply(2, 6);
Console.WriteLine($"Product: {product}");
int quotient = calculator.Divide(20, 5);
Console.WriteLine($"Quotient: {quotient}");
}
Yukarıdaki örnekte, Calculator
sınıfı basit bir hesap makinesini temsil eder. Toplama, çıkarma, çarpma ve bölme işlemlerini gerçekleştiren metotları bulunur.
KISS prensibine göre, kodun basit ve anlaşılır olması önemlidir. Bu nedenle, her bir matematiksel işlem için ayrı ayrı ve net bir şekilde adlandırılmış metotlar kullanılmıştır. Karmaşık algoritmalar veya gereksiz kod blokları yerine, basit ve doğrudan anlaşılabilir bir yapı tercih edilmiştir.
Ana metotta, Calculator
sınıfı örneklenir ve farklı hesaplamalar yapılır. Her işlem sonucu, sonuçlar ekrana yazdırılır.
DRY (Don't Repeat Yourself)
DRY prensibine göre, aynı veya benzer kod bloklarını tekrar etmek yerine, bu kod blokları bir kez yazılmalı ve tekrar kullanılmalıdır. Bu sayede, kodun sadece bir yerden güncellenmesi ve tutarlılığın sağlanması mümkün olur.
DRY (Don't Repeat Yourself) prensibinin C# dilinde detaylı bir örneği:
public class Rectangle
{
public int Width { get; set; }
public int Height { get; set; }
public int CalculateArea()
{
return Width * Height;
}
public int CalculatePerimeter()
{
return 2 * (Width + Height);
}
}
public class Square
{
public int SideLength { get; set; }
public int CalculateArea()
{
return SideLength * SideLength;
}
public int CalculatePerimeter()
{
return 4 * SideLength;
}
}
public static void Main(string[] args)
{
Rectangle rectangle = new Rectangle();
rectangle.Width = 5;
rectangle.Height = 3;
int rectangleArea = rectangle.CalculateArea();
int rectanglePerimeter = rectangle.CalculatePerimeter();
Console.WriteLine($"Rectangle Area: {rectangleArea}");
Console.WriteLine($"Rectangle Perimeter: {rectanglePerimeter}");
Square square = new Square();
square.SideLength = 4;
int squareArea = square.CalculateArea();
int squarePerimeter = square.CalculatePerimeter();
Console.WriteLine($"Square Area: {squareArea}");
Console.WriteLine($"Square Perimeter: {squarePerimeter}");
}
Yukarıdaki örnekte, Rectangle
ve Square
sınıfları şekillerin alanını ve çevresini hesaplamak için kullanılır.
Ancak, yukarıdaki örnekte DRY prensibi ihlal edilmiştir. Rectangle
ve Square
sınıflarının CalculateArea
ve CalculatePerimeter
metotları aynı mantığı içerse de ayrı ayrı tekrar yazılmıştır. Bu durum, kod tekrarına ve tutarsızlığa neden olur.
DRY prensibine uygun olarak, aynı işlevselliğe sahip kod blokları bir kez yazılmalı ve tekrar kullanılmalıdır. Aşağıda, DRY prensibine uygun şekilde güncellenmiş bir örnek verilmiştir:
public class Shape
{
public virtual int CalculateArea()
{
return 0;
}
public virtual int CalculatePerimeter()
{
return 0;
}
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public override int CalculateArea()
{
return Width * Height;
}
public override int CalculatePerimeter()
{
return 2 * (Width + Height);
}
}
public class Square : Shape
{
public int SideLength { get; set; }
public override int CalculateArea()
{
return SideLength * SideLength;
}
public override int CalculatePerimeter()
{
return 4 * SideLength;
}
}
public static void Main(string[] args)
{
Rectangle rectangle = new Rectangle();
rectangle.Width = 5;
rectangle.Height = 3;
int rectangleArea = rectangle.CalculateArea();
int rectanglePerimeter = rectangle.CalculatePerimeter();
Console.WriteLine($"Rectangle Area: {rectangleArea}");
Console.WriteLine($"Rectangle Perimeter: {rectanglePerimeter}");
Square square = new Square();
square.SideLength = 4;
int squareArea = square.CalculateArea();
int squarePerimeter = square.CalculatePerimeter();
Console.WriteLine($"Square Area: {squareArea}");
Console.WriteLine($"Square Perimeter: {squarePerimeter}");
}
Yukarıdaki örnekte, Shape
sınıfı tüm şekillerin temel özelliklerini içerir ve CalculateArea
ve CalculatePerimeter
metotlarını sanal olarak tanımlar. Rectangle
ve Square
sınıfları, Shape
sınıfını kalıtım alır ve bu metotları override eder. Bu sayede, aynı işlevselliği tekrar tekrar yazmak yerine, bu metotlar bir kez tanımlanır ve her şekil sınıfında kullanılır.
DRY prensibi, kodun tekrarını önler, kod karmaşıklığını azaltır ve bakım kolaylığı sağlar. Böylece, yazılımın sürdürülebilirliği artar ve kodun genel kalitesi iyileşir.
Sağlıkla ve esenlikle kalın.