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,
invelist.remove()gibi koleksiyon işlemlerinin__eq__tanımından etkilenebileceğini açıklayabilmeli,assertile ü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
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:
Bookhangi durumu koruyor?Memberhangi koleksiyonu yönetiyor ve neden tuple görünümü döndürüyor?Libraryneden kitap ve üyeleri sözlükte tutuyor?Library.borrow()hangi işleri kendi yapıyor, hangilerini nesnelere devrediyor?- Aynı
Booknesnesi hemLibrary.booksiçinde hem üyenin koleksiyonunda bulunabilir mi? Bu iki ayrı kitap anlamına gelir mi? - 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:
- Üye ve kitap bulunur,
- Üyenin ödünç alma koşulları kontrol edilir,
- Kitabın uygunluğu kontrol edilir,
- Ancak bundan sonra iki durum değiştirilir.
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.isbnArtık:
first = Book("978-1", "Python")
second = Book("978-1", "Aynı ISBN, başka nesne")
print(first is second) # False
print(first == second) # Trueve dolayısıyla second in [first] sonucu da True olur.
__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) == 1kullanabiliriz. 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.
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,
- kitap reddedilir,
- reddedilen kitap hâlâ kullanılabilirdir,
- üyenin koleksiyonunda yalnızca üç kitap vardır.
Gereksinim 2 — İade işlemi
İ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_booksbeklenir. 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:
- Aynı ISBN’li iki farklı nesne
==ile eşit,isile farklı olmalı. - Aynı ISBN’li ikinci nesne, üyelik kontrolünde ilk nesneyle eşleşmeli.
remove()davranışının artık ISBN eşitliğinden etkilendiğini gösterin.- 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:
- Ödünç alma sınırı neden
Membersorumluluğudur? Libraryneden yine de ön koşulları koordine eder?Book.__eq__eklemek koleksiyon davranışını nasıl değiştirdi?- İç koleksiyon neden doğrudan mutable liste olarak dışarı verilmedi?
- Hangi kontroller
raise, hangileriassertile ifade edildi ve neden?
Kendinizi kontrol edin
- Tek nesnenin invariant’ı ile nesneler arası tutarlılık arasındaki fark nedir?
book in bookshangi eşitlik mekanizmasından etkilenir?list.remove(book)neden__eq__eklenince farklı davranabilir?==ileisfarkı bu modelde neden önemlidir?assertneden üretim girdisi doğrulaması için güvenilir değildir?- Bir iç koleksiyonun tuple olarak sunulması hangi kapsülleme sorununu azaltır?
- 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ıinveremove()gibi koleksiyon davranışlarını da etkiler.- Kimlik ve alan eşitliği farklı sorulardır.
assertgeliştirici/test kontrolüdür;python -Oile 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.