Nesne Yönelimli Programlamaya Giriş: Basit Banka Hesabı Uygulaması (Python)

Programlama Paradigmaları

Nesne Yönelimli Programlamaya Giriş: Basit Banka Hesabı Uygulaması (Python)

Programlama Paradigmaları
7 dk okuma süresi
Bu yeni başlayanlar için kodlama rehberi, Python’da nesne yönelimli programlama (OOP) mantığını sınıflar ve nesneler üzerinden öğretir. Basit bir BankAccount (banka hesabı) örneğini adım adım yazar, kapsülleme yaklaşımını ve temel test fikrini gösterir. Örnek, öğrenim amaçlıdır ve gerçek finansal sistem gereksinimlerini karşılamaz.
Nesne Yönelimli Programlamaya Giriş: Basit Banka Hesabı Uygulaması (Python)

Hedef: Bu yazı, nesne yönelimli programlamaya (OOP) yeni başlayanlar için, Python’da sınıflar ve nesneler fikrini somutlaştıran küçük bir “banka hesabı” uygulamasıyla ilerler. Kodları yazarken hem kavramı öğrenecek hem de küçük adımlarla nasıl daha güvenilir hale getirileceğini göreceksiniz.

Önemli not: Buradaki BankAccount örneği yalnızca eğitim amaçlıdır. Gerçek bankacılık/finans yazılımları; kimlik doğrulama, işlem bütünlüğü, denetim izleri, yetkilendirme, eşzamanlılık, uyumluluk ve güvenlik gibi kapsamlı gereksinimler ister. Bu örnek, bu gereksinimleri kapsamak için tasarlanmamıştır.


OOP nedir ve neden işinize yarar?

Nesne yönelimli programlama, veriyi (durumu) ve bu veri üzerinde çalışan davranışı (metotları) tek bir “nesne” içinde birleştirerek programı modellemeyi amaçlayan bir yaklaşımdır. Yeni başlayanlar için bunun büyük avantajı, gerçek dünyadaki kavramları (hesap, kullanıcı, sepet, sipariş gibi) koda daha düzenli şekilde taşıyabilmektir.

Python’da sınıfların nasıl tanımlandığı ve örneklerin (instance) nasıl çalıştığı, resmi dokümantasyonda anlatılır: Python Tutorial: Classes.

Bu yazıda hangi OOP parçalarını göreceksiniz?

  • Sınıf (class): Bir şablon/tip tanımı.
  • Nesne (object/instance): Sınıftan üretilmiş gerçek örnek.
  • Öznitelikler (attributes): Nesnenin taşıdığı veriler (örn. bakiye).
  • Metotlar (methods): Nesnenin yaptığı işler (örn. para yatırma/çekme).
  • Kapsülleme (encapsulation): Durumu doğrudan değiştirmek yerine kontrollü metotlar üzerinden yönetme.

Neden “Banka Hesabı” örneği?

“BankAccount” türü örnekler; durum yönetimi (balance/bakiye), davranış (deposit/withdraw) ve doğrulama (negatif tutar engeli, yetersiz bakiye) gibi OOP’nin temel faydalarını kısa bir kodla göstermeye elverişlidir. Bu yaklaşım, öğretim materyallerinde de sık kullanılan bir örnek olarak karşınıza çıkar.


1) Minimum gereksinimleri birlikte belirleyelim

Başlamadan önce küçük bir “iş listesi” çıkarmak, sınıf tasarımını kolaylaştırır:

  • Hesabın bir sahibi (owner) olsun.
  • Hesabın bir bakiyesi (balance) olsun.
  • deposit(amount): Pozitif tutarda para yatırabilsin.
  • withdraw(amount): Pozitif tutarda para çekebilsin; bakiye yetersizse engellesin.
  • Bakiye, dışarıdan rastgele değiştirilmesin; kontrollü bir API üzerinden yönetilsin.

2) BankAccount sınıfını yazalım (temel sürüm)

Python’da sınıf tanımlamak için class anahtar kelimesini, nesne oluştururken ilk değerleri ayarlamak için çoğunlukla __init__ metodunu kullanırız. Bu yapı, Python’un “Classes” bölümünde detaylandırılır: docs.python.org.

Aşağıdaki örnekte “kapsülleme” için Python’daki yaygın yaklaşımı da kullanacağız: bakiyeyi _balance gibi tek alt çizgiyle işaretleyerek “doğrudan değiştirmeyin; metotlar üzerinden yönetin” konvansiyonunu uygularız.

Python örnek kodu: BankAccount (temel)

class BankAccount:
    def __init__(self, owner: str, balance: int = 0) -> None:
        if balance < 0:
            raise ValueError("Balance cannot be negative")
        self.owner = owner
        self._balance = balance

    def get_balance(self) -> int:
        return self._balance

    def deposit(self, amount: int) -> int:
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self._balance += amount
        return self._balance

    def withdraw(self, amount: int) -> int:
        if amount <= 0:
            raise ValueError("Withdraw amount must be positive")
        if amount > self._balance:
            raise ValueError("Insufficient funds")
        self._balance -= amount
        return self._balance

Bu kodda OOP açısından neler oldu?

  • Durum (state): self._balance hesabın anlık durumunu tutar.
  • Davranış (behavior): deposit/withdraw, bu durumu kontrollü şekilde değiştirir.
  • Kurallar: Negatif/0 tutar engeli ve yetersiz bakiye kontrolü, sınıfın içinde “iş kuralı” olarak durur.

3) Python’da kapsülleme: “private” neden tam değil?

Python, bazı dillerdeki gibi katı “private/public” erişim belirleyicilerini zorunlu kılmaz. Bunun yerine:

  • _tek_alt_cizgi genellikle “dışarıdan dokunmayın” anlamına gelen bir konvansiyondur.
  • __cift_alt_cizgi ise ad dönüştürme (name mangling) ile çakışmaları azaltır; yine de “mutlak gizleme” garantisi olarak düşünülmez.

Daha fazla okuma için: Python Classes ve Real Python: OOP in Python.


4) Küçük bir kullanım senaryosu

Şimdi bu sınıfı “bir kullanıcı hesabı açtı, para yatırdı, para çekti” gibi basit bir senaryoda kullanalım. (Aşağıdaki satırlar, bir Python dosyasında sınıfın altına eklenebilir.)

Örnek kullanım

acc = BankAccount(owner="Taylor", balance=100)
acc.deposit(50)   # 150
acc.withdraw(20) # 130
acc.get_balance() # 130

Para temsili notu: Burada bakiyeyi int tuttuk (öğrenmeyi basitleştirir). Gerçek para hesaplarında genellikle kuruş/cent gibi “en küçük birim” üzerinden tamsayı kullanmak veya Decimal tercih etmek daha güvenli bir yaklaşımdır; float ile para hesabından kaçınmak iyi bir kuraldır.


5) Tasarımı bir adım iyileştirelim: özel hata tipi

Hatanın türünü ayırmak, hatayı yakalayan kodun niyetini netleştirir. “Yetersiz bakiye”yi ayrı bir istisna ile ifade edebilirsiniz:

Özel istisna ve güncellenmiş withdraw

class InsufficientFundsError(Exception):
    pass

class BankAccount:
    ...
    def withdraw(self, amount: int) -> int:
        if amount <= 0:
            raise ValueError("Withdraw amount must be positive")
        if amount > self._balance:
            raise InsufficientFundsError("Insufficient funds")
        self._balance -= amount
        return self._balance

Böylece çağıran kod, “bu gerçekten bakiye sorunu mu?” ayrımını daha açık şekilde yapabilir.


6) @dataclass kullanmalı mıyım?

Python’da dataclasses, daha çok “veri taşıyan” sınıflarda tekrar eden kodu azaltmak için kullanılır. Bizim örneğimizde davranış (deposit/withdraw) ön planda olduğu için klasik sınıf tanımı gayet anlaşılır olabilir.

  • Klasik class: Tam kontrol, açık başlangıç mantığı, eğitim için net.
  • @dataclass: Alan tanımı ve bazı metotları (örn. temsil) otomatikleştirerek yazımı kısaltabilir; fakat iş kuralları yine metotlarda kalır.

7) Öğrenmeyi hızlandıran pratik: küçük testler yazın

Küçük adımlarla ilerlemek ve basit testler eklemek, hataları erken yakalamanıza yardımcı olur. Aşağıdaki örnek, 5. bölümdeki InsufficientFundsError sürümünü temel alır:

Basit unit test fikri (örnek)

import unittest

class TestBankAccount(unittest.TestCase):
    def test_deposit_increases_balance(self):
        acc = BankAccount("Taylor", 100)
        acc.deposit(50)
        self.assertEqual(acc.get_balance(), 150)

    def test_withdraw_reduces_balance(self):
        acc = BankAccount("Taylor", 100)
        acc.withdraw(40)
        self.assertEqual(acc.get_balance(), 60)

    def test_withdraw_insufficient_funds(self):
        acc = BankAccount("Taylor", 10)
        with self.assertRaises(InsufficientFundsError):
            acc.withdraw(999)

Bu testler “tam kapsam” iddiası taşımaz; amaç, OOP örneğini yazarken davranışı düzenli olarak doğrulama alışkanlığı kazanmaktır.


8) OOP’yi derinleştirmek için 3 küçük geliştirme görevi

Kodunuzu bir anda büyütmek yerine, küçük görevlerle ilerleyin:

  1. İşlem geçmişi ekleyin: deposit/withdraw çağrılarında bir listeye (örn. self._transactions) kayıt atın.
  2. Okunabilir temsil: __repr__ veya __str__ ekleyerek hesabı yazdırdığınızda anlamlı çıktı üretin.
  3. Basit miras: SavingsAccount gibi bir alt sınıf oluşturup “faiz uygula” gibi ek bir metot ekleyin.

9) Üretim sistemiyle karıştırmayın: hızlı kontrol listesi

Bu tür eğitsel “bank account” örnekleri üretim beklentilerini karşılamak üzere tasarlanmaz. Üretimde benzer bir alanla çalışacaksanız en azından şu başlıklara ayrıca bakmanız gerekir:

  • Kimlik doğrulama ve yetkilendirme
  • Girdi doğrulama (tutar, para birimi, sınırlar, tipler)
  • Eşzamanlılık ve atomiklik
  • Denetim izi ve kayıt
  • Hata yönetimi (kullanıcı mesajı vs. iç hata)
  • Test stratejisi (birim + entegrasyon + sınır durumları)

10) Daha fazla öğrenme için kaynaklar


Sonuç

Bu yazıda Python’da OOP’nin temel taşlarını “BankAccount” üzerinden kurduk: sınıf/nesne, durum/davranış, kapsülleme konvansiyonu, doğrulama kuralları, özel hata tipi ve basit test fikri. Sonraki adım olarak, sınıfı küçük görevlerle genişletip her eklemeyi testlerle doğrulamayı deneyin.