Yazılarımız

Veri Akademi

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 projesinde sınıf diyagramı, katmanlar ve sorumluluk ayrımını anlatan çalışma masası düzeni

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 Class

VB.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 Class

Inheritance 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
Try Catch blokları ve özel exception sınıfıyla hatayı sınıflandıran VB.NET kod ekranı ve log çıktısı

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.

  1. Domain: Entity, Value Object, Domain exception, domain servisleri
  2. Application: Use-case odaklı servisler, DTO’lar, doğrulama akışları
  3. Infrastructure: Veritabanı erişimi, e-posta/HTTP entegrasyonları, dosya sistemi
  4. 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.

 VERİ AKADEMİ