Gözetimli bütünleştirici nesne tasarımı

Son hafta yeni bir OOP kavramı eklemiyoruz. Amaç dönem boyunca öğrendiğimiz fikirleri küçük ve sınırları açık bir değişiklikte birlikte kullanmak: nesne durumunu anlamak, sorumluluğu doğru sınıfa vermek, geçerli durumu korumak, nesneler arası ilişkiyi yönetmek ve değişikliğin etkisini açıklamak.

Bu hafta neleri yapabilmelisiniz?

  • Hazır küçük bir sınıf modelini okuyabilmeli,
  • Yeni gereksinimin hangi sınıfın sorumluluğuna ait olduğunu belirleyebilmeli,
  • Gereksiz yeni sınıf veya kalıtım oluşturmadan davranış ekleyebilmeli,
  • Invariant’ı koruyan değişiklik yapabilmeli,
  • Birden fazla nesneyi etkileyen değişikliklerde tutarlı durumu koruyabilmeli,
  • in ve list.remove() gibi koleksiyon işlemlerinin __eq__ tanımından etkilenebileceğini açıklayabilmeli,
  • assert ile üretim doğrulamasını birbirinden ayırabilmeli,
  • Değişikliğin nedenini ve sınırını kısa teknik dille açıklayabilmelisiniz.

Başlangıç sistemi

WarningBaşlangıç kodu “nihai üretim mimarisi” değildir

Bu model öğretim amacıyla küçüktür. Gerçek bir sistemde birden fazla nesneyi etkileyen işlemler, kalıcı veri ve eşzamanlılık olduğunda transaction gibi daha güçlü mekanizmalar gerektirebilir. Buradaki hedef, önce doğrula → sonra değiştir ilkesini ve sorumluluk sınırlarını görünür hâle getirmektir.

Önce sistemi okuyun

Kod eklemeden şu soruları cevaplayın:

Problem alanındaki farklı sorumlulukların uygun sınıflara dağıtılmasını gösteren tasarım diyagramı.

Nesne sorumlulukları
  1. Book hangi durumu koruyor?
  2. Member hangi koleksiyonu yönetiyor ve neden tuple görünümü döndürüyor?
  3. Library neden kitap ve üyeleri sözlükte tutuyor?
  4. Library.borrow() hangi işleri kendi yapıyor, hangilerini nesnelere devrediyor?
  5. Aynı Book nesnesi hem Library.books içinde hem üyenin koleksiyonunda bulunabilir mi? Bu iki ayrı kitap anlamına gelir mi?
  6. Hangi kontroller iki nesne değişmeden önce yapılmalıdır?

Nesneler arası tutarlılık

Tek nesnenin invariant’ını korumak yeterli olmayabilir. Ödünç alma gibi bir işlem hem Book hem Member durumunu değiştirir. İkinci güncelleme başarısız olup birinci değişiklik yapılmış kalırsa sistem yarım güncellenir.

Bu yüzden bu örnekte:

  1. Üye ve kitap bulunur,
  2. Üyenin ödünç alma koşulları kontrol edilir,
  3. Kitabın uygunluğu kontrol edilir,
  4. Ancak bundan sonra iki durum değiştirilir.
ImportantBir sınıfın invariant’ı yetmeyebilir

Bir kullanım senaryosu birden fazla nesnenin durumunu birlikte değiştiriyorsa nesneler arası tutarlılık da ayrı bir tasarım sorusudur.

Kritik sentez: in ve remove() hangi eşitliği kullanıyor?

Şu iki satıra dikkat edin:

if book in self._borrowed_books:
    ...

self._borrowed_books.remove(book)

Liste üyelik kontrolü (in) ve list.remove() eşleşmeyi == eşitliği üzerinden arar. Kendi sınıfımızda __eq__ tanımlamadığımız başlangıç durumunda farklı Book örnekleri, ISBN’leri aynı olsa bile alan anlamında eşit sayılmaz.

Şimdi 12. haftadaki gibi ISBN eşitliği ekleyelim:

class Book:
    ...

    def __eq__(self, other):
        if not isinstance(other, Book):
            return NotImplemented
        return self.isbn == other.isbn

Artık:

first = Book("978-1", "Python")
second = Book("978-1", "Aynı ISBN, başka nesne")

print(first is second)  # False
print(first == second)  # True

ve dolayısıyla second in [first] sonucu da True olur.

Warning__eq__ eklemek yalnızca a == b satırını değiştirmez

Eşitlik tanımı, in, list.remove(), liste karşılaştırmaları ve başka koleksiyon işlemlerinin davranışını da değiştirebilir. Book.__eq__’yi ISBN’e göre tanımladığınız anda Member sınıfındaki üyelik/çıkarma mantığının semantiği de sessizce değişir.

Bu kötü olmak zorunda değildir; hatta alan modeliniz için istediğiniz davranış olabilir. Ancak etkiyi bilinçli test etmeniz gerekir.

Mikro deney — eşitliğin yan etkisini görün

Burada remove(probe), listede aynı nesne bulunmasa bile ISBN’e göre eşit kabul edilen registered nesnesini kaldırır. Bu davranışın modeliniz için doğru olup olmadığını tasarım kararı olarak değerlendirin.

assert ne için kullanılmalı?

Ders boyunca küçük beklentileri görünür kılmak için:

assert book.borrowed_by is member
assert len(member.borrowed_books) == 1

kullanabiliriz. Bu, test düşüncesine iyi bir geçiştir.

Ancak assert üretim girdisi doğrulama mekanizması değildir:

assert amount > 0, "Tutar pozitif olmalı"

şeklinde iş kuralı korumak güvenli değildir. Python -O (optimize) seçeneğiyle çalıştırıldığında assert ifadeleri kaldırılabilir.

ImportantInvariant için raise, geliştirici kontrolü için assert

Kullanıcı girdisi, alan kuralı veya nesnenin geçerli durumunu koruyan kontrol normal program davranışının parçasıysa if ...: raise ... kullanın. assert, “programcı olarak burada doğru olduğunu varsaydığım şey gerçekten doğru mu?” türündeki geliştirme/test kontrolleri için uygundur.

Gereksinim 1 — Üye başına en fazla üç kitap

MAX_BORROWED_BOOKS = 3 kuralının Member içinde tutulması güçlü bir adaydır; çünkü üyenin kendi ödünç kitap koleksiyonunun sahibidir. Ancak koordinasyonu yapan Library, durum değişikliklerinden önce member.ensure_can_borrow(book) çağırarak yarım güncelleme riskini azaltır.

Şunları test edin:

  • 1., 2. ve 3. kitap başarıyla alınır,
    1. kitap reddedilir,
  • reddedilen kitap hâlâ kullanılabilirdir,
  • üyenin koleksiyonunda yalnızca üç kitap vardır.

Gereksinim 2 — İade işlemi

Bir kullanım senaryosunda nesnelerin kendi sorumlulukları üzerinden metot çağrılarıyla işbirliği yapmasını gösteren akış.

Nesneler arası işbirliği

İade başlamadan önce şu durumları düşünün:

  • Üye yok,
  • Kitap yok,
  • Kitap zaten kütüphanede,
  • Kitap başka bir üyede,
  • Kitap üyeyi gösteriyor ama üyenin listesinde bulunmuyor.

Başarılı iade sonunda:

assert book.is_available
assert book not in member.borrowed_books

beklenir. Bu assertlar test kontrolüdür; return_book() içindeki iş kurallarının yerine geçmez.

Gereksinim 3 — ISBN’e göre eşitlik

Book.__eq__ davranışını ISBN’e göre ekleyin. Sonra özellikle aşağıdaki regresyonları çalıştırın:

  1. Aynı ISBN’li iki farklı nesne == ile eşit, is ile farklı olmalı.
  2. Aynı ISBN’li ikinci nesne, üyelik kontrolünde ilk nesneyle eşleşmeli.
  3. remove() davranışının artık ISBN eşitliğinden etkilendiğini gösterin.
  4. Bu davranışın kütüphane modelinizde istenen semantik olup olmadığını kısa gerekçeyle açıklayın.

Bu görev 6. haftadaki kimlik, 9. haftadaki nesne koleksiyonu ve 12. haftadaki eşitlik bilgisini aynı yerde birleştirir.

Gözetimli bütünleştirici görev

Başlangıç sistemine şu küçük değişiklikleri uygulayın:

  • Üye başına en fazla üç kitap kuralını doğrulayın,
  • Güvenli iade davranışını tamamlayın,
  • Book.__eq__’yi ISBN üzerinden tanımlayın,
  • İç koleksiyonu dışarıdan doğrudan değiştirmeyi engelleyin,
  • Başarılı ve başarısız akışları küçük kontrollerle doğrulayın.

Çalışır kod teslimi zorunludur. En az şu senaryoları otomatik olarak kontrol eden çalışır bir dosya teslim edin:

  • Normal ödünç alma,
  • Aynı kitabı tekrar alma,
  • Dördüncü kitap sınırı,
  • Başarılı iade,
  • Yanlış üyeden iade,
  • Aynı ISBN’li farklı Book örnekleriyle eşitlik/üyelik davranışı.

Alan kurallarını assert ile değil raise ile koruyun; assertları yalnızca test/sonuç doğrulaması için kullanın.

Kısa tasarım savunması

Kodun altına en fazla 150 kelimelik cevap ekleyin:

  1. Ödünç alma sınırı neden Member sorumluluğudur?
  2. Library neden yine de ön koşulları koordine eder?
  3. Book.__eq__ eklemek koleksiyon davranışını nasıl değiştirdi?
  4. İç koleksiyon neden doğrudan mutable liste olarak dışarı verilmedi?
  5. Hangi kontroller raise, hangileri assert ile ifade edildi ve neden?

Kendinizi kontrol edin

  1. Tek nesnenin invariant’ı ile nesneler arası tutarlılık arasındaki fark nedir?
  2. book in books hangi eşitlik mekanizmasından etkilenir?
  3. list.remove(book) neden __eq__ eklenince farklı davranabilir?
  4. == ile is farkı bu modelde neden önemlidir?
  5. assert neden üretim girdisi doğrulaması için güvenilir değildir?
  6. Bir iç koleksiyonun tuple olarak sunulması hangi kapsülleme sorununu azaltır?
  7. Koordinatör sınıf ile alan nesnelerinin sorumluluklarını nasıl ayırıyoruz?

Bu haftadan akılda kalması gerekenler

  • İyi nesne tasarımı yalnızca sınıf yazmak değil, durum değişimlerinin sorumluluğunu ve sırasını yönetmektir.
  • Birden fazla nesneyi değiştiren işlemde nesneler arası tutarlılık ayrıca düşünülmelidir.
  • __eq__ tanımı in ve remove() gibi koleksiyon davranışlarını da etkiler.
  • Kimlik ve alan eşitliği farklı sorulardır.
  • assert geliştirici/test kontrolüdür; python -O ile kaldırılabildiği için iş kuralı doğrulaması değildir.
  • Kapsülleme mutable iç koleksiyonları da kapsar.
  • Dönemin hedefi yeni terim ezberlemek değil, küçük bir değişikliği gerekçeli, çalışır ve doğrulanmış biçimde yapabilmektir.
Back to top