VB.NET İLE NESNE YÖNELİMLİ PROGRAMLAMA: CLASS, INTERFACE VE EXCEPTİON YÖNETİMİ
VB.NET ile sürdürülebilir bir uygulama geliştirmek istiyorsanız, “çalışıyor” olmasının ötesinde bakımı kolay, genişletilebilir ve hatalara karşı dayanıklı bir tasarım kurgulamanız gerekir. Nesne yönelimli programlama (OOP) bu kurguyu sistemli biçimde kurmanızı sağlar: doğru sınıf sınırları, net sorumluluklar, tutarlı sözleşmeler ve kontrollü hata yönetimi.
Bu yazıda VB.NET’te class tasarımını, interface ile bağımlılıkları yönetmeyi ve exception stratejileriyle güvenilir akışlar kurmayı adım adım ele alacağız. Örnekleri gerçekçi bir senaryo üzerinden ilerletip, kod organizasyonu ve pratik tasarım kararlarını da konuşacağız.
Hedefimiz; “tek dosyada her şey” yaklaşımından uzaklaşıp, domain model, servis katmanı ve hata yönetimi gibi konuları bilinçli ele alan bir OOP taslağı oluşturmak. Eğer kapsamlı bir yol haritası isterseniz VB.NET eğitimi sayfasındaki modüller de iyi bir tamamlayıcı olur.
VB.NET nesne yönelimli programlama temelleri ve primary keyword
Bu makaledeki primary keyword’ümüz: VB.NET nesne yönelimli programlama. OOP, “veri + davranış” birlikteliğini sınıflarla modelleyerek karmaşıklığı yönetir. VB.NET tarafında bu yaklaşım; encapsulation (kapsülleme), inheritance (kalıtım) ve polymorphism (çok biçimlilik) gibi yapı taşlarıyla pratikte karşılık bulur.
OOP’yi sadece dil özelliği gibi değil, kodun büyümesini yöneten bir tasarım disiplini olarak düşünmek önemlidir. Bu sayede “değişiklik geldiğinde neresi kırılır?” sorusunu daha erken cevaplarsınız. Özellikle kurumsal uygulamalarda katmanlı mimari VB.NET yaklaşımıyla sınıfların nerede duracağı, hangi katmanın neyi bilmesi gerektiği kritik hale gelir.
OOP’yi tasarım dili gibi kullanmak
Uygulamanın domain’ini (iş kuralları) konuşabildiğiniz bir model kurduğunuzda, geliştirici ekip içinde ortak bir dil oluşur. Örneğin “Müşteri”, “Sipariş”, “Ödeme” gibi kavramlar sınıf olarak yaşayabilir ve iş kuralları bu sınıfların davranışlarına dağıtılabilir. Bu yaklaşım, gereksiz veri taşıma nesnelerinin artmasını engeller ve kodun niyetini görünür kılar.
Encapsulation ile sınır çizmek
Kapsülleme, dış dünyaya sadece gerekli yüzeyi açıp detayları saklamaktır. VB.NET’te Private, Protected ve Public erişim belirleyicileri; sınıfın iç düzenini korur. “Her şey public olsun” kolay görünse de, zamanla sınıfın iç tutarlılığını bozarak hataları artırır.

VB.NET class yapısı: Domain model ve sorumluluk tasarımı
“VB.NET class yapısı” genelde ilk öğrenilen başlık olsa da, asıl değer sınıfın neyi temsil ettiği ve hangi sorumluluğu taşıdığıyla ortaya çıkar. Domain model tarafında sınıflar, iş kavramlarını ve kurallarını taşır. Bu katmanda gereksiz altyapı bağımlılıklarını minimize etmek; test edilebilirlik ve taşınabilirlik açısından güçlü bir avantajdır.
İyi bir sınıf tasarımında, alanların (fields) ve özelliklerin (properties) tutarlı olması beklenir. Örneğin bir siparişin toplam tutarını dışarıdan rastgele set etmek yerine; satır kalemlerine göre hesaplatmak hem daha güvenli hem daha anlamlıdır. Böylece sınıfın iç durumu korunur ve hatalı kullanım zorlaşır.
Property ve constructor kararları
Özelliklerinizin ReadOnly olması gereken yerleri belirlemek, yanlış akışların önünü keser. Constructor ile zorunlu alanları mecbur kılmak da benzer şekilde “geçersiz nesne” üretimini azaltır. Bu yaklaşım, ileride oluşabilecek domain hatalarını daha erken yakalamanızı sağlar.
Örnek: Basit bir müşteri modeli
Aşağıdaki modelde, müşteri oluşturmak için minimum bilgiler istenir ve e-posta formatı gibi kurallar tek bir yerde kontrol edilir. Bu tarz kuralların UI katmanında da doğrulanması faydalıdır, ancak gerçek kaynak sınıfın kendisi olmalıdır.
Public Class Customer
Public ReadOnly Property Id As Guid
Public Property FullName As String
Public Property Email As String
Public Sub New(fullName As String, email As String)
If String.IsNullOrWhiteSpace(fullName) Then
Throw New ArgumentException("Ad Soyad boş olamaz.")
End If
If String.IsNullOrWhiteSpace(email) OrElse Not email.Contains("@") Then
Throw New ArgumentException("E-posta formatı geçersiz.")
End If
Id = Guid.NewGuid()
FullName = fullName.Trim()
Email = email.Trim().ToLowerInvariant()
End Sub
End ClassVB.NET interface kullanımı: Sözleşme ile bağımlılık yönetimi
“VB.NET interface kullanımı” özellikle servis katmanında ve dış bağımlılıkların yönetiminde oyunun kurallarını değiştirir. Interface, bir sınıfın ne yapacağını tanımlar; nasıl yaptığını değil. Bu sayede farklı implementasyonları kolayca değiştirebilir, birim testlerde sahte (fake/mock) bileşenler kullanabilirsiniz.
Örneğin e-posta gönderen bir bileşenin doğrudan SMTP kütüphanesine bağlı olmasını istemezsiniz. Bunun yerine bir IEmailSender sözleşmesi tanımlayıp, gerçek ortamda SMTP implementasyonu, testte ise bellek içi bir implementasyon kullanmak daha sağlıklıdır.
Interface ile SOLID prensiplerine yaklaşmak
Interface kullanımı, özellikle Dependency Inversion yaklaşımını destekler: üst seviye modüller, alt seviye detaylara değil; soyutlamalara bağlı kalır. Bu da “değişiklik geldiğinde yalnızca bir noktayı güncelleme” olasılığını yükseltir. Buradaki hedef, soyutlamayı gereksiz yere çoğaltmak değil; değişme ihtimali yüksek bağımlılıkları kontrol altına almaktır.
Örnek: Sözleşme ve iki farklı implementasyon
Bu örnekte IEmailSender hem test hem de üretim tarafında farklı biçimde kullanılabilir. Ayrıca servis sınıfı sadece sözleşmeye bağlı kaldığı için daha esnek hale gelir.
Public Interface IEmailSender
Sub Send(toAddress As String, subject As String, body As String)
End Interface
Public Class SmtpEmailSender
Implements IEmailSender
Public Sub Send(toAddress As String, subject As String, body As String) Implements IEmailSender.Send
' Gerçek sistemde SMTP ayarları ve gönderim kodu burada olur.
Console.WriteLine($"SMTP ile gönderildi: {toAddress} - {subject}")
End Sub
End Class
Public Class InMemoryEmailSender
Implements IEmailSender
Public ReadOnly Property SentMessages As New List(Of String)
Public Sub Send(toAddress As String, subject As String, body As String) Implements IEmailSender.Send
SentMessages.Add($"{toAddress}|{subject}|{body}")
End Sub
End Class
Public Class CustomerNotificationService
Private ReadOnly _sender As IEmailSender
Public Sub New(sender As IEmailSender)
_sender = sender
End Sub
Public Sub Welcome(customer As Customer)
Dim subject = "Hoş geldiniz"
Dim body = $"Merhaba {customer.FullName}, hesabınız oluşturuldu."
_sender.Send(customer.Email, subject, body)
End Sub
End ClassInheritance ve polymorphism: Ne zaman kalıtım, ne zaman kompozisyon?
Kalıtım (inheritance) ve çok biçimlilik (polymorphism) OOP’nin güçlü araçlarıdır, ancak yanlış kullanıldığında karmaşıklığı artırabilir. “Her şeyi base class yapalım” yaklaşımı, ortak olmayan davranışları bile miras zincirine zorla yerleştirebilir. Bunun yerine kompozisyon (bileşim) çoğu senaryoda daha sade bir model sunar.
Yine de ortak davranışlar netse ve “is-a” ilişkisi gerçek anlamda doğruysa kalıtım faydalıdır. Örneğin farklı ödeme yöntemleri aynı sözleşmeyi uygulayabilir: kredi kartı, havale, dijital cüzdan gibi. Burada polymorphism; çağıran tarafın ayrıntıları bilmeden tek bir arayüzle çalışmasını sağlar.
Abstract sınıflar ve overridable davranış
VB.NET’te MustInherit sınıflar ve MustOverride metotlar ile “temel akış” çizip, detayları alt sınıflara bırakabilirsiniz. Ancak bu yaklaşımı seçerken, alt sınıfların gerçekten ortak akışı paylaşacağından emin olun. Aksi halde “kırılgan base class” problemi doğar.
Kompozisyonun avantajı
Kompozisyon; bir sınıfın davranışı başka bir sınıfa “sahip olarak” kullanmasıdır. Örneğin loglama, önbellek, doğrulama gibi çapraz kesen kaygılar (cross-cutting concerns) genellikle kompozisyonla daha iyi yönetilir. Bu yaklaşım, sınıf hiyerarşisini şişirmeden, davranışı gerektiği yerde kullanmanızı sağlar.
VB.NET exception handling: Try-Catch stratejisi ve hata sözlüğü
“VB.NET exception handling” konusu, kullanıcı deneyimi ve sistemin güvenilirliği açısından merkezi bir rol oynar. Exception’ları “saklamak” yerine; sınıflandırmak, doğru seviyede yakalamak ve anlamlı şekilde raporlamak gerekir. Aksi halde ya her yerde Try...Catch görürsünüz ya da hiçbir yerde yoktur ve uygulama rastgele çöker.
Genel prensip: exception’ı, gerçekten ele alabileceğiniz katmanda yakalayın. Örneğin domain katmanında bir kural ihlali olduğunda özel bir exception fırlatıp, UI/API katmanında bunu kullanıcıya uygun bir mesajla yansıtmak mantıklıdır. Bunun yanında loglama ve izleme de unutulmamalıdır; kullanıcıya nazik olmak, teknik detayı kaybetmek demek değildir.
Özel exception sınıflarıyla anlam katmak
Özel exception’lar, hatayı daha okunur ve yönetilebilir kılar. “ArgumentException” her zaman yeterli olmayabilir; özellikle iş kuralları söz konusuysa. Örneğin “Kredi limiti yetersiz” hatasını ayrı bir türle temsil etmek, hem raporlama hem de üst katmanda karar verme açısından daha net bir model sağlar.
Örnek: Custom exception ve kontrollü yakalama
Aşağıdaki örnekte bir ödeme senaryosu düşünelim. Domain tarafında kural ihlali olduğunda özel bir exception fırlatılıyor; servis tarafında ise sadece bu durum yakalanıp yönetiliyor. Diğer hatalar üst seviyeye iletilerek merkezi bir hata yakalama mekanizmasına bırakılabilir.
Public Class InsufficientBalanceException
Inherits Exception
Public Sub New(message As String)
MyBase.New(message)
End Sub
End Class
Public Class Wallet
Public Property Balance As Decimal
Public Sub New(initialBalance As Decimal)
Balance = initialBalance
End Sub
Public Sub Debit(amount As Decimal)
If amount <= 0D Then
Throw New ArgumentException("Tutar pozitif olmalı.")
End If
If Balance < amount Then
Throw New InsufficientBalanceException("Bakiye yetersiz olduğu için işlem tamamlanamadı.")
End If
Balance -= amount
End Sub
End Class
Public Class PaymentService
Public Function Pay(wallet As Wallet, amount As Decimal) As Boolean
Try
wallet.Debit(amount)
Return True
Catch ex As InsufficientBalanceException
' Kullanıcıya iş kuralı mesajı gösterilebilir, log seviyesine göre kaydedilebilir.
Console.WriteLine(ex.Message)
Return False
End Try
End Function
End Class
Katmanlı mimari VB.NET: Exception, logging ve sınırların korunması
Katmanlı mimaride her katmanın sorumluluğu farklıdır: UI/API kullanıcıyla konuşur, uygulama servisleri akışı yönetir, domain katmanı kuralları taşır, altyapı katmanı dış sistemlerle haberleşir. Exception yönetimi de bu sınırları ihlal etmeyecek şekilde kurgulanmalıdır.
Örneğin domain katmanı; veritabanı bağlantısı, HTTP çağrısı, dosya sistemi gibi ayrıntıları bilmemelidir. Bu tip hatalar genellikle altyapı katmanında oluşur. Domain katmanına doğru “teknik exception” sızdırmak yerine; uygun şekilde sarmalayıp (wrap) ya da uygulama servislerinde ele alıp kullanıcıya anlaşılır bir yanıt üretmek daha doğrudur.
Hata türlerini sınıflandırma yaklaşımı
Pratikte exception’ları üç grupta düşünmek işinizi kolaylaştırır: (1) Kullanıcı hataları ve doğrulama sorunları, (2) İş kuralı ihlalleri, (3) Sistem/altyapı hataları. Bu sınıflandırma, log seviyesini ve kullanıcıya dönecek mesaj biçimini belirlemede yardımcı olur.
Liste: Uygulanabilir exception prensipleri
- Genel Catch kullanımı yerine, mümkün olduğunca spesifik exception türlerini yakalayın.
- Hata mesajını kullanıcıya gösterirken teknik detayları ayırın; teknik detayları loglayın.
- Domain kuralı ihlallerini özel exception türleriyle ifade edin.
- Exception’ı yutmayın; gerçekten ele almıyorsanız yeniden fırlatın veya üst katmana bırakın.
- Merkezi bir hata yakalama (global handler) yaklaşımıyla tekrarı azaltın.
Pratik tasarım ipuçları: Test edilebilirlik, okunabilirlik ve sürdürülebilirlik
OOP’nin hedefi, sadece “sınıf yazmak” değildir. Kodun değişime dayanıklı olması için test edilebilirlik ve okunabilirlik şarttır. Interface kullanımı burada kritik bir kaldıraçtır; çünkü dış bağımlılıkları soyutlar. Aynı şekilde sınıfların küçük ve odaklı olması, Single Responsibility ilkesine yaklaşmanızı sağlar.
Okunabilirlik için isimlendirme ve dosya organizasyonu göz ardı edilmemelidir. Sınıfları “UserManager1”, “Helper2” gibi belirsiz adlarla çoğaltmak yerine, domain kavramlarını ve gerçek sorumlulukları yansıtan adlar seçin. Ayrıca, namespace düzeniyle katmanları belirginleştirmek, ekip içinde ortak bir navigasyon sağlar.
Önerilen klasör/namespace düzeni
Aşağıdaki düzen, küçük-orta ölçekli uygulamalarda netlik sağlar. Elbette proje ölçeğine göre uyarlamak gerekir.
- Domain: Entity, Value Object, Domain exception, domain servisleri
- Application: Use-case odaklı servisler, DTO’lar, doğrulama akışları
- Infrastructure: Veritabanı erişimi, e-posta/HTTP entegrasyonları, dosya sistemi
- Presentation: UI veya API controller katmanı
Küçük ama etkili kontrol listesi
Günlük geliştirmede aşağıdaki kontrol listesi, tasarımın zamanla bozulmasını engeller:
- Bir sınıfın amacı tek cümleyle anlatılamıyorsa, muhtemelen çok sorumluluk taşıyordur.
- Bir method “10 parametre” istiyorsa, modellemeyi yeniden düşünün.
- Interface eklerken, değişim ihtimali olmayan yerlerde gereksiz soyutlama üretmeyin.
- Exception mesajlarını anlamlı tutun; “Error occurred” gibi genel mesajlardan kaçının.
Sonuç: Class + Interface + Exception üçlüsüyle güçlü VB.NET OOP
VB.NET’te nesne yönelimli programlama; doğru class tasarımıyla domain’i netleştirir, interface ile bağımlılıkları gevşetir ve exception yönetimiyle güvenilir akış kurar. Bu üçlü birlikte çalıştığında, kod hem daha anlaşılır hem de değişime daha dayanıklı hale gelir.
Bir sonraki adım olarak, mevcut projenizde küçük bir modülü seçip sınıf sorumluluklarını sadeleştirerek başlayabilirsiniz: önce domain kurallarını sınıflara taşıyın, sonra dış bağımlılıkları interface ile soyutlayın, en son exception stratejisini katman sınırlarına göre düzenleyin. Böylece kademeli refactor ile risk almadan iyileştirme yapabilirsiniz.


