Nesne Yönelimli Programlama (OOP), gerçek dünyadaki varlıkları sınıf ve nesne mantığıyla modelleyerek kodun okunabilirliğini, bakımı ve yeniden kullanılabilirliğini artırır. E-ticaret uygulamalarında, örneğin Product (ürün), Order (sipariş) veya Customer (müşteri) gibi varlıkları doğrudan nesne olarak temsil etmek daha mantıklı ve organize bir yapı sunar. Bu sayede, ürün ekleme, stok güncelleme veya sipariş oluşturma gibi işlemleri aynı konsept altında yönetebiliriz.
- Önemli OOP Dilleri: Java, C#, C++, Python, vb.
E-ticaret sistemlerinde ödeme (payment) ve kargo (shipping) gibi farklı operasyonlar düşünün. Sizin “ödeme” yapma işlemini nasıl yapacağınızı henüz bilmeseniz de neyi yapacağınızı (parayı çekme, onaylama vs.) belirlemek istiyorsanız, Interface size “sözleşme” sunar. “Kredi kartı ödemesi” veya “Banka kartı ödemesi” gibi sınıflar, bu interface’i uygulayarak farklı ödeme yöntemleri oluşturabilir.
Interface:
- Metotların ne yapacağını söyler (genelde gövdesiz).
- Bir sınıf birden fazla interface’i
implementedebilir. - Java 8’den itibaren “default” ve “static” metotlar ile gövdeli metotlar da eklenebilir.
Abstract Class:
- Hem soyut (
abstract) hem de gövdeli metotlar içerebilir. - Sadece tek bir abstract class genişletilebilir (
extends). - Ortak özellikleri, constructor’ları ve alanları tutarak alt sınıflar için bir iskelet sunar.
Kısaca, benzer davranışları paylaşmak isteniyorsa Abstract Class, farklı türde ama benzer arayüzlere sahip işlemler için ise Interface kullanılabilir.
Java’da iki nesnenin bellek adresi yerine, “içerik” veya “kimlik” bazında eşit olup olmadığını kontrol etmek için equals metodunu override ederiz. E-ticarette sıklıkla ürün nesnelerinin productId alanına göre eşitlik kontrolü yapılır. Örneğin iki farklı Product nesnesi, eğer aynı productId değerine sahipse “aynı ürün” olarak kabul edilir.
public class Product {
private String productId;
private String name;
// Constructor vs.
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Product)) return false;
Product other = (Product) obj;
return this.productId.equals(other.productId);
}
@Override
public int hashCode() {
return productId.hashCode();
}
}
hashCode ise HashMap veya HashSet gibi hash tabanlı koleksiyonlarda verimli arama ve ekleme için kullanılır. Eğer equals metodunu değiştirirsek, aynı zamanda hashCode metodunu da tutarlı olacak şekilde override etmemiz gerekir.
Diamond problemi, bir sınıfın, farklı ata sınıflarda tanımlanmış aynı metodu (veya özelliği) miras aldığı zaman ortaya çıkan belirsizliktir. C++ gibi dillerde birden fazla sınıfı doğrudan kalıtım yoluyla genişletmek (multiple inheritance) bu sorunu yaratır. Java’da ise sınıflarda çoklu kalıtım desteklenmediği için diamond problemi büyük ölçüde ortadan kalkar.
Ancak interface’lerin default metotları ile benzer bir çakışma oluşabilir. İki interface aynı default metodu sağlıyorsa, o metodu implement eden sınıf içerisinde açıkça override etmek gerekir:
interface Shippable {
default void ship() {
System.out.println("Shippable interface’inden gönderim yapılıyor.");
}
}
interface Trackable {
default void ship() {
System.out.println("Trackable interface’inden gönderim yapılıyor.");
}
}
class Order implements Shippable, Trackable {
@Override
public void ship() {
// Hangi interface'in ship metodu çalışacak?
Shippable.super.ship();
Trackable.super.ship();
}
}
Bu şekilde hangisinin kullanılacağı belirtilebilir.
Java’da bellek yönetimini otomatik olarak gerçekleştiren Garbage Collector (GC), artık referans verilmeyen nesneleri tespit ederek bellekten temizler. E-ticaret sitelerinde sürekli yeni Product, Order nesneleri oluşturup sonradan bunlara ihtiyaç duymayabiliriz. GC bu gereksiz nesneleri temizleyerek bellek tasarrufu sağlar.
- Nasıl Çalışır? JVM periyodik olarak heap üzerinde “ulaşılamayan” nesneleri işaretler ve temizler.
- Ne Zaman Çalışır? Tam olarak zamanı garanti edilmemekle birlikte, bellek kullanımı belirli bir eşiğe ulaşınca veya JVM uygun gördüğünde devreye girer.
System.gc()ile “GC isteği” yapılsa bile derhal çalışacağının garantisi yoktur. JVM’in inisiyatifindedir.
static, bir üyeyi sınıf seviyesine taşır, örnek oluşturmaya gerek kalmadan erişilebilir kılar. E-ticaret projelerinde örneğin, fiyat hesaplama gibi genel araç metotlar için “utility” sınıflarda static tanımlanabilir:
public class PriceUtils {
public static double applyDiscount(double price, double discountRate) {
return price * (1 - discountRate);
}
}
staticdeğişkenler tüm örneklerce ortak kullanılır.staticbloklar ise sınıf yüklendiğinde bir defaya mahsus çalışır.staticmetotlar üzerinden, sınıfın instance üyelerine doğrudan erişilemez.
Bir nesnenin immutability özelliği, oluşturulduktan sonra iç durumunun değiştirilememesi anlamına gelir. Mesela e-ticarette “para birimi ve tutar” gibi bir değerin yanlışlıkla değiştirilmesini istenmez. Immutable yapılırsa, “fiyat” bir kez belirlendiğinde daha sonradan farklı yerlerde manipüle edilemez.
- Nerede Kullanılır? Sıkça
String,BigDecimalgibi Java sınıflarında görülür. Finansal işlemlerde ve verinin paylaşıldığı çok katmanlı sistemlerde faydalıdır. - Nasıl Yapılır? Tüm alanları
private finaltanımlanır, setter metot verilmez, varsa koleksiyon alanları savunma kopyasıyla saklanır. - Neden? thread-safe olurlar, paylaşımda beklenmedik değişikliklerin önüne geçilir. Ayrıca kodu anlamak ve test etmek daha kolaydır.
- Composition: Bir nesne diğerinin “yaşam döngüsüne” sıkı sıkıya bağlıdır. Örneğin, “Sipariş” içindeki “Sipariş Kalemleri (OrderItem)” nesneleri olmadan siparişin tam bir anlamı yoktur. Sipariş silinirse, içindeki Sipariş Kalemleri de silinir.
- Aggregation: Daha gevşek bir bağlılık vardır. Örneğin, “Müşteri” ile “Adres” ilişkisi. Müşteri silinse bile adres kayıtları başka yerlerde kullanılabiliyor olabilir ya da adresler sistemde ayrı tutulabilir.
Composition = güçlü sahiplik, Aggregation = zayıf sahiplik.
- Cohesion (Uyumluluk): Bir sınıfın veya modülün içindeki sorumlulukların birbirine ne kadar odaklı olduğu ile alakalı bir kavramdır. Örneğin
InventoryServicesadece stok yönetimi ile ilgili metotlar içeriyorsa “yüksek cohesion” vardır. - Coupling (Bağlılık): Sınıfların veya modüllerin birbirine ne ölçüde bağımlı olduğu ile alakalı bir kavramdır.
OrderServicesınıfının,ProductService’in ayrıntılarına çok fazla bağımlı olması yüksek coupling’e yol açar. Değiştirirken kırılma riski artar.
İyi mimarilerde hedef, yüksek cohesion ve düşük couplingtir. Bu sayede kod daha sürdürülebilir, anlaşılır ve genişletilebilir olur.
- Stack: Metot çağrıları, yerel değişkenler ve referanslar tutulur. Fonksiyonun (metot) çalışması tamamlandığında buradaki alan otomatik silinir. Okuma-yazma çok hızlıdır ancak alanı sınırlıdır.
- Heap: Nesnelerin bellekte tutulduğu geniş bir havuzdur. Java’nın Garbage Collector’ı ile yönetilir.
newanahtar sözcüğü ile oluşturduğumuz nesneler genellikle heap’te yer alır. Erişimi stack’e göre daha yavaştır ancak çok daha büyük bir alan sunar.
Özellikle büyük e-ticaret projelerinde çok sayıda nesne (ürün, kullanıcı, sipariş) heap’te yer alır, stack ise metot çağrısı anında kısa ömürlü bilgileri saklar.
Exception, çalışma sırasında oluşan beklenmedik hatalardır.
- Checked Exception: Derleyici tarafından kontrol edilir,
try-catchveyathrowsile yönetmek gerekirIOException,SQLExceptiongibi hatalar, e-ticaret sisteminizde veritabanına bağlanırken ortaya çıkabilecek hatalardır. - Unchecked (Runtime) Exception: Kodumuz çalışırken ortaya çıkar, derleyici bu hataları yakalayamaz (
NullPointerException,IndexOutOfBoundsExceptionvb.). Örneğin,Cartlistesinde olmayan bir ürüne erişmeye çalışmak. - Error: JVM’in kritik hataları (
OutOfMemoryErrorgibi). Uygulama düzeyinde yakalayıp devam etmek genelde mümkün değildir.
E-ticarette kullanıcı hatalarını, ödemeyle ilgili hataları veya veritabanı sorunlarını (örneğin stok tablosuna erişim) uygun şekilde yönetmek için exception'ları kullanmak önemlidir.
“Clean code”, okuyan kişinin niyetinizi hızla anlayabileceği, bakımı ve genişletmesi kolay, gereksiz karmaşıklıklardan arındırılmış koddur. İyi adlandırmalar, tek sorumluluk prensibi, gereksiz tekrarları önleme (SOLID, DRY, KISS, YAGNI) ve açık, net yapılandırma önemlidir. Böylece hem ekibe yeni katılanlar hem de kodu sonradan inceleyecek olanlar için zahmetsizce (technical debt) geliştirilebilir ve sürdürülebilir bir proje ortaya çıkar.
Eğer bir alt sınıfta, üst sınıftaki static metodun aynısını (aynı imza ile) tekrar tanımlanırsa, bu işlem “method hiding” (metot gizleme) olarak adlandırılır. Yani bu bir “override” değildir çünkü static metotlar sınıfa aittir. Derleme zamanı (compile time) hangi metot çağrılacağına karar verilir.
class Campaign {
public static void info() {
System.out.println("Kampanya bilgisi: Üst sınıf statik metot.");
}
}
class SpecialCampaign extends Campaign {
public static void info() {
System.out.println("Özel kampanya bilgisi: Alt sınıf statik metot.");
}
}
public class Test {
public static void main(String[] args) {
Campaign campaign = new SpecialCampaign();
campaign.info(); // "Kampanya bilgisi: Üst sınıf statik metot." (method hiding)
SpecialCampaign.info(); // "Özel kampanya bilgisi: Alt sınıf statik metot."
}
}
Statik metotlar nesneye göre değil, değişkenin sınıf tipine göre çalışır.
- Abstraction (Soyutlama): Bir sınıfın “ne yapacağını” tanımlar, “nasıl yapacağını” gizler. Örneğin bir
Paymentinterface’indepay()metodu tanımlandığında, nasıl ödeme alınacağı alt sınıfların (kredi kartı, PayPal, kapıda ödeme vb.) sorumluluğundadır. - Polymorphism (Çok Biçimlilik): Aynı arayüz veya metodun farklı alt sınıflarda farklı davranışlar göstermesidir. Mesela
pay()metodu,CreditCardPaymentiçin ayrı,PayPalPaymentiçin ayrı çalışır. Kod “genel” bir referansla (Payment payment;) yazılsa bile, gerçek nesneye göre farklı metot gövdeleri çalışır (runtime polymorphism).
Bu ikili birlikte kullanıldığında (abstract/interface + override) e-ticaret sistemlerinde farklı ödeme yöntemlerini veya farklı kargo tiplerini tek bir çatı altında toplayabilir, yeni özellikler eklemeyi kolaylaştırır.