Kompozisyon, delegation ve nesne koleksiyonları

Gerçek programlar tek bir sınıftan oluşmaz. Bir siparişin müşterisi ve ürünleri, bir kütüphanenin kitapları ve üyeleri vardır. Örneklerin çoğu bir internet mağazasının sipariş programından: müşteri, ürün ve sipariş için ayrı sınıflar yazacak, sonra bunları birbirine bağlayacağız. Bu hafta bir nesnenin başka nesneleri niteliklerinde tutmasını (has-a, yani “sahiptir” ilişkisi), sınıfları bu yolla birleştirmeyi (kompozisyon) ve bir nesnenin bir hesabı, o hesabın kuralını bilen başka bir nesneye bırakmasını (delegation) göreceğiz.

Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.

Bu bölümün kapsamı

NotSınavda sorulur
  • Nesnenin başka nesneyi tuttuğu has-a ilişkisi
  • Koleksiyonun nesneleri değil referanslarını tutması
  • Değiştirilebilir (mutable) iç koleksiyonu dışarı vermenin kapsüllemeyi bozması
  • İç listeyi dışarıya demet ya da kopya olarak vermek
  • Davranışı kuralın sahibine bırakan delegation
  • Sorumluluğu yanlış sınıfa yığmanın sonucu
  • Bire çok ilişkide liste ile sözlük seçimi
  • UML’deki aggregation ve composition ayrımının biçimsel tanımı
  • ORM (nesneleri veritabanı tablolarına eşleyen araçlar) ve kalıcı veri katmanı
  • collections.abc koleksiyon protokolleri

Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.

Bir nesne başka nesneyi tutabilir

C’de bir struct’ın içine başka bir struct koyduğunuzda iç yapının alanları kopyalanırdı. Hücredeki Order, bir Customer nesnesini customer niteliğinde tutuyor. Sipariş müşterinin bir kopyasını mı tutuyor, yoksa aynı nesneyi mi? Son satır ne yazar?

self.customer = customer ataması kopya almaz: 3. haftada gördüğünüz gibi = var olan nesneye bir ad daha bağlar. C’de aynı davranışı struct’ın içinde müşteriye bir işaretçi tutarak elde ederdiniz.

İngilizcede bu ilişki Order has a Customer (sipariş bir müşteriye sahiptir) diye okunur. has-a adı buradan gelir. Bir sınıfın işini, niteliklerinde tuttuğu başka nesnelerle yapmasına kompozisyon (composition) denir.

Bir nesnenin başka bir nesneyi içererek has-a ilişkisi kurmasını gösteren kompozisyon diyagramı.

Kompozisyon ve has-a ilişkisi
NotKompozisyon ve agregasyon terimleri

Bu derste kompozisyon sözcüğünü geniş anlamda kullanıyoruz: bir sınıfın işini, niteliklerinde tuttuğu başka nesnelerle yapması. UML (sınıfları ve aralarındaki ilişkileri kutu ve okla çizmek için kullanılan standart gösterim) ve bazı kaynaklar burada iki ilişkiyi ayırır. Parça bütünle birlikte yok oluyorsa, örneğin sipariş silinince sipariş satırları da gidiyorsa buna composition (bileşim) denir. Parça bütünden bağımsız yaşıyorsa, örneğin takım dağılınca oyuncular yerinde kalıyorsa buna aggregation (agregasyon) denir. Bu ayrım sınavda sorulmaz. Nesneler arasındaki ilişkiyi doğru kurmanız ve her kuralı doğru sınıfa koymanız yeter.

Nesne koleksiyonu

C’de siparişin ürünlerini bir struct dizisinde tutar, toplamı döngüyle hesaplardınız. Python’da sipariş ürünleri bir listede tutar. Hücrede klavye siparişe eklendikten sonra fiyatı 1000’den 900’e iniyor. order.total() kaç döndürür?

_items bir sayı listesi değildir, Product nesnelerinin referanslarını tutar. keyboard adı da listedeki ilk eleman da aynı nesneyi gösterir. total() her çağrıldığında ürünlerin o anki fiyatına bakar.

Bir koleksiyonun nesnelerin kendilerini değil nesne referanslarını tuttuğunu gösteren diyagram.

Nesne koleksiyonu

İç listeyi dışarı vermek kapsüllemeyi bozabilir

Siparişe ürün eklemenin yolu add_product() olmalı, çünkü kontrol orada. Hücredeki add_product aynı ürünün iki kez eklenmesini engelliyor. Kısa kalsın diye ürünleri adlarıyla tutuyoruz. Liste _items adıyla duruyor ve dışarıya bir property ile veriliyor. Dışarıdaki kod order.items.append("Klavye") yazıyor. Bu satır hata verir mi? Son satır ne yazar?

Property listeyi olduğu gibi döndürdü. Dış kod sınıfın listesine referans aldı ve add_product() metoduna hiç uğramadan ekleme yaptı. items düz bir nitelik olsaydı (self.items = []) sonuç yine aynı olurdu.

Dış kodun listeyi yalnız okuyabilmesini istiyorsanız bir demet döndürebilirsiniz. Bu kez property demet döndürüyor. Son satırda ne olur?

Demetin append metodu yoktur. Dış kod listeyi okuyabilir, ama eleman eklemenin tek yolu yine add_product().

Dış kod üzerinde değişiklik yapabileceği ayrı bir liste istiyorsa sığ kopya döndürebilirsiniz. Kopyaya eklenen eleman iç listeye gitmez:

@property
def items(self):
    return self._items.copy()

Hangisinin uygun olduğu, sınıfın çağırana ne söz verdiğine bağlıdır. Demet “bu listeyi değiştirmeyin” der, kopya “bu liste artık sizin” der.

Önemli3. ve 8. haftadaki iki fikir burada buluşuyor

3. haftada aliasing gördük: iki ad aynı değiştirilebilir nesneyi gösterince birinden yapılan değişiklik ötekinden de görünür. 8. haftada nesnenin durumunu yalnız sınıfın kendi metotlarıyla değiştirmeyi öğrendik, çünkü kurallar o metotlarda denetlenir. İç listeyi dışarı verdiğinizde dış kod sınıfın listesine referans alır. Artık add_product() gibi denetleyen metotlara hiç uğramadan listeyi değiştirebilir.

Delegation: işi sahibine bırakmak

Sipariş satırı (OrderLine) bir ürünü ve adedini tutar. Toplu alım indirimi ise ürünün fiyat kuralıdır: Product.price_for() metodu 10 adet ve üstüne yüzde 10 indirim uygular. Satırın tutarını hesaplayan subtotal() bu metodu çağırıyor. İki satır ne yazar?

5 kalemde indirim yoktur, discount 0 olur ve sonuç 10 * 5 * 1 = 50 çıkar. 12 kalemde discount 0.10 olur. 1 - 0.10 bir ondalık sayı (float) olduğu için sonuç da float olur: 108.0.

OrderLine fiyat kuralını kopyalamaz, hesabı Product.price_for() metoduna devreder. Bir nesnenin bir işi, o işin kuralını bilen başka bir nesneye bırakmasına delegation denir. İndirim kuralı değişirse yalnız Product sınıfını güncellersiniz. C’de satır tutarını hesaplayan fonksiyonun, ürünün fiyat fonksiyonunu çağırması gibi düşünebilirsiniz.

ÖnemliHer çağrıyı devretmek gerekmez

Delegation, davranışı kuralın sahibi olan sınıfa koyar. İndirim kuralı ürüne aitse hesabı Product yapar. Ürüne özgü bir kural yoksa, örneğin ara toplam yalnız birim fiyatla adedin çarpımıysa, bu çarpmayı OrderLine içinde yapmak da makuldür.

Sorumluluk yanlış yerdeyse ne olur?

Sipariş verilince ürünün stoğu düşmeli. Bu işi Order kendisi yapabilir:

class Order:
    def add_line(self, product, quantity):
        if quantity > product._stock:
            raise ValueError("Yetersiz stok")
        product._stock -= quantity
        self._lines.append(OrderLine(product, quantity))
        # kargo ücreti, müşteri puanı, kampanya kuralları...

Kod çalışır, ama stok kuralı artık iki yerde yazılı: 8. haftada aynı kuralı Product.sell() içine yazmıştık. Order ürünün alt çizgili niteliğine dışarıdan dokunuyor. Kargo, puan ve kampanya kuralları da buraya eklendikçe sınıf kısa sürede “her şeyi yapan” bir merkeze dönüşür. Stoğu düşmek ürünün işidir:

class Order:
    def add_line(self, product, quantity):
        product.sell(quantity)
        self._lines.append(OrderLine(product, quantity))

Order artık yalnız işlemin sırasını yönetir: önce ürüne satışı yaptırır, sonra satırı ekler. Stok yetmezse sell() hata verir ve satır da eklenmez.

Bire çok ilişki ve koleksiyon seçimi

  • Tek nesne: order.customer
  • Çok sayıda nesne: order.lines

Bir siparişin tek müşterisi vardır, onu tek bir nitelik tutar. Satırları ise çoktur, onları bir koleksiyon tutar. Bir nesnenin aynı türden çok sayıda nesne tuttuğu bu ilişkiye bire çok ilişki denir. Koleksiyonun liste mi sözlük mü olacağına, ona nasıl erişeceğiniz karar verir.

Mağazanın müşterilerini tutan bir Shop sınıfı düşünün. C’de müşteriyi numarasıyla bulmak için diziyi baştan sona tarardınız. Sözlükte ise anahtar müşteri numarası, değer Customer nesnesi olur ve müşteriye taramadan ulaşırsınız. Hücrede 103 numaralı müşteri yok. Üç satır ne yazar?

Aynı numaranın iki kez eklenmesini Shop engeller, çünkü bütün numaraları yalnız koleksiyonu tutan sınıf görür. Tek bir Customer nesnesi öteki müşterileri bilmez.

C ile Python yan yana

Sipariş programındaki işlerin C’deki ve Python’daki karşılıkları:

İş C’de Bu haftaki Python yolu Dikkat
Müşteriyi ve ürünleri tutup toplamı hesaplamak struct içinde müşteri ve ürün dizisi, toplam için döngü self.customer niteliği, self._items listesi ve total() metodu Liste ürünlerin kopyasını değil referansını tutar. Ürünün fiyatı değişirse toplam da değişir
Ürünü yalnız kontrolden geçirerek eklemek add_item() fonksiyonu yazılır, ama diziye her kod yazabilir add_product() metodu ve _items listesi Property self._items döndürürse dış kod kontrolü atlar. Demet ya da kopya döndürün
İndirim kuralını uygulamak Satır tutarını hesaplayan fonksiyon ürünün fiyat fonksiyonunu çağırır subtotal() içinde self.product.price_for(self.quantity) Kuralı satıra kopyalamayın, ürüne bırakın
Stoğu düşmek Sipariş fonksiyonu ürünün stok alanını azaltır add_line() içinde product.sell(quantity) Order ürünün _stock niteliğine dokunmaz
Müşteriyi numarasıyla bulmak Diziyi baştan sona tararsınız self._customers sözlüğü ve find_customer() Tekrar kontrolünü koleksiyonu tutan Shop yapar

C’de müşteri struct’ını siparişin içine koysaydınız müşterinin bir kopyasını tutmuş olurdunuz: müşterinin adı değişince sipariş eski adı gösterirdi. Python’da sipariş aynı müşteri nesnesini gösterir. Ürün eklerken C ile Python aynı yerde zorlanır: diziyi ya da listeyi dışarı açarsanız kontrolü atlayan kodu hiçbir şey durdurmaz. Python’da çözüm, listeyi dışarı hiç vermemek ya da demet veya kopya olarak vermektir. Stoğu sipariş fonksiyonunda azaltmak C’de de Python’da da çalışır, ama stok kuralı iki yerde yazılmış olur.

Alıştırma: İlişkiyi belirle

Car ve Engine, Order ve Customer, Playlist ve Song, Student ve Course çiftlerine tek tek bakın. İlişki has-a mı? Öyleyse hangi sınıfın ötekini tutması uygun? Tartışın. Sonra şunu düşünün: ilişkiyi iki tarafta birden saklarsanız, yani öğrenci derslerini, ders de öğrencilerini tutarsa, bir öğrenci dersi bıraktığında hangi iki yeri güncellemeniz gerekir? İkisinden birini unutursanız ne olur?

Alıştırma: Nesne koleksiyonunu tamamla

Team sınıfı oyuncuları bir listede tutuyor. add_player aynı numaralı oyuncunun iki kez eklenmesini engelliyor. Kontroldeki any(...), parantezin içindeki koşul listedeki oyunculardan en az biri için doğruysa True döndürür. Kontrol geçtikten sonra oyuncuyu iç listeye ekleyen satırı boşluğa yazın.

self._players.append(player)

Sıra sizde: Kütüphane modeli

Book, Member ve Library sınıflarıyla küçük bir model kurun:

  • Her kitap ISBN’iyle ayırt edilsin.
  • Aynı ISBN iki kez eklenemesin.
  • Üye numarası benzersiz olsun.
  • Library kitapları ve üyeleri koleksiyonlarda tutsun.
  • Library bu değiştirilebilir koleksiyonları doğrudan dışarı vermesin.
  • Ödünç alma davranışı hangi sınıfa ya da sınıflara ait? Gerekçesiyle karar verin.

Çalışır kod görevi: İki kitap ve iki üye ekleyin. Aynı ISBN’i ya da aynı üye numarasını ikinci kez eklemeye çalışınca hata alındığını assert ve try/except ile doğrulayın. Ayrıca dışarıya verdiğiniz koleksiyona append() yapılamadığını ya da yapılan eklemenin iç koleksiyona yansımadığını gösterin.

Sınav provası

Önce kendi cevabınızı seçin; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu okuyun. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav soruları da bu mantıkla yazılır.

1. Aşağıdaki program ne yazdırır?

class Customer:
    def __init__(self, name):
        self.name = name

class Order:
    def __init__(self, customer):
        self.customer = customer

customer = Customer("Ada")
order = Order(customer)
customer.name = "Ece"

print(order.customer.name)
  • A. Customer
  • B. Ada
  • C. AttributeError
  • D. None
  • E. Ece

2. Aşağıdaki program ne yazdırır?

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

class Order:
    def __init__(self):
        self._items = []

    def add_product(self, product):
        self._items.append(product)

    def total(self):
        result = 0
        for product in self._items:
            result += product.price
        return result

order = Order()
order.add_product(Product("Klavye", 1000))
order.add_product(Product("Mouse", 500))
print(order.total())
  • A. 2
  • B. TypeError; nesneler doğrudan toplanamaz
  • C. None
  • D. 0
  • E. 1500

3. Aşağıdaki tasarımda order.items.append(product) çağrısı neden sorunludur?

class Order:
    def __init__(self):
        self.items = []

    def add_product(self, product):
        # tekrar, stok veya başka kurallar
        self.items.append(product)
  • A. Sorun yoktur; add_product() yine de çağrılır
  • B. append başka bir sınıfın listesine nesne ekleyemez
  • C. Dışarıdan yapılan ekleme listenin sırasını bozduğu için
  • D. add_product() atlandığı için içindeki kontroller çalışmaz
  • E. items bir property olmadığı için AttributeError verir

4. Aşağıdaki property iç listeyi gerçekten korur mu?

@property
def items(self):
    return self._items
  • A. Evet; Python property dönüşlerini kendiliğinden kopyalar
  • B. Hayır; dış kod dönen listeye yine append yapabilir
  • C. Evet; property ile döndürülen değerler salt okunur olur
  • D. Evet; _items adı tek alt çizgili olduğu için korunur
  • E. Hayır; ama @items.setter eklenirse sorun çözülür

5. İç listeyi dışarıya yalnızca okunacak biçimde vermek isteyen property hangisini döndürmelidir?

  • A. return len(self._items)
  • B. return tuple(self._items)
  • C. return list(self._items) (kopya)
  • D. return self._items
  • E. return self._items.copy

6. Aşağıdaki tasarımda OrderLine.subtotal() neden fiyatlandırma kuralını kendisi yazmaz?

class OrderLine:
    def __init__(self, product, quantity):
        self.product = product
        self.quantity = quantity

    def subtotal(self):
        return self.product.price_for(self.quantity)
  • A. OrderLine sınıfı Product sınıfından türetildiği için
  • B. Metot çağırmak, çarpmayı yerinde yazmaktan hızlı olduğu için
  • C. Fiyat kuralı Product sınıfına ait olduğu için
  • D. Hesap OrderLine içinde yazılırsa AttributeError oluşacağı için
  • E. Delegation her çağrıda zorunlu olduğu için

7. Aşağıdaki tasarımın uzun vadede sorunu nedir?

class Library:
    def borrow_book(self, member, book):
        book.is_borrowed = True
        member.borrowed_count += 1
        # onlarca kural...
  • A. Sorun yoktur; koordinasyon sınıfı bütün kuralları taşımalıdır
  • B. borrowed_count bütün üyeler için ortak bir sınıf niteliği olmalıdır
  • C. Library kitap ve üyenin kurallarını da taşıyan bir merkeze dönüşür
  • D. Bir metot iki farklı nesneyi parametre olarak alamaz
  • E. is_borrowed niteliği dışarıdan değiştirilemez

8. Aşağıdaki Library tasarımını düzeltmek için en uygun adım nedir?

class Library:
    def borrow_book(self, member, book):
        book.is_borrowed = True
        member.borrowed_count += 1
        # onlarca kural...
  • A. Kuralları Book ve Member metotlarına taşıyıp Library içinde çağırmak
  • B. Book ve Member sınıflarını kaldırıp her şeyi sözlükte tutmak
  • C. borrow_book metodunu modül düzeyinde bir fonksiyona taşımak
  • D. is_borrowed ve borrowed_count niteliklerini property yapmak
  • E. Library sınıfını Book ve Member sınıflarından türetip kuralları devralmak

9. Üye numarasından doğrudan erişim gerekiyorsa Library._members hangi yapıda tutulmalıdır?

  • A. Sözlük; anahtar üye numarası, değer Member nesnesi olur
  • B. Demet; üye listesi dışarıdan değiştirilmemelidir
  • C. Liste; üyeler eklenme sırasıyla durduğu için arama kolaydır
  • D. Küme; her üye numarası yalnız bir kez bulunabildiği için
  • E. Her üye için Library üzerinde ayrı bir nitelik

10. Bir otobüs seferinde Trip sınıfı yolcuları _passengers koleksiyonunda tutuyor. Yolcular sefer boyunca biniş sırasıyla ekleniyor. Program yolcuları yalnız biniş sırasıyla yazdırıyor, tek bir yolcuyu numarasıyla hiç aramıyor. Bu kullanım için hangi koleksiyon uygundur?

  • A. Her yolcu için Trip üzerinde ayrı bir nitelik
  • B. Demet; yolcuların biniş sırası sonradan değişmemelidir
  • C. Sözlük; bire çok ilişkiyi tutan koleksiyon sözlük olmalıdır
  • D. Liste; yolcular eklendikçe sona girer ve sırayla dolaşılır
  • E. Küme; her yolcu nesnesi koleksiyonda yalnız bir kez bulunur

1. Doğru: E. Kompozisyonda nesne kopyalanmaz, referansı tutulur: order.customer ile customer aynı Customer nesnesini gösterir. customer.name değişince order.customer.name da yeni değeri verir.

  • A yanlış: Yazdırılan, sınıf adı değil nitelik değeridir.
  • B yanlış: Order oluşturulurken ad Ada olduğu için nesnenin o anki hâliyle kopyalandığı sanılabilir. Ama Python argümanı kopyalamaz; aynı nesne paylaşılır.
  • C yanlış: order.customer.name iki adımlı bir erişim olduğu için geçersiz sanılabilir. Ama zincirleme nitelik erişimi geçerlidir.
  • D yanlış: name niteliği bir metne bağlıdır.

2. Doğru: E. _items Product nesnelerine referans tutar; total() her nesnenin price niteliğini toplar.

  • A yanlış: Toplanan fiyatlardır, eleman sayısı değil.
  • B yanlış: Toplanan nesneler değil, product.price değerleridir.
  • C yanlış: total() bir sayı döndürür.
  • D yanlış: İki ürün gerçekten eklenmiştir.

3. Doğru: D. Dış kod iç listeye doğrudan erişir ve add_product() metoduna hiç uğramadan ekleme yapar; metottaki kontroller de çalışmaz. Dış kod ile nesne aynı listeyi paylaştığı için listeyi değiştiren her satır nesnenin durumunu da değiştirir.

  • A yanlış: Metodun içinde de append yazdığı için iki yolun aynı olduğu sanılabilir. Oysa doğrudan append çağrısı metodu hiç çalıştırmaz.
  • B yanlış: Liste başka bir nesneye ait olduğu için ona dışarıdan eklenemeyeceği sanılabilir. Ekleyebilir; sorun da budur.
  • C yanlış: Eleman beklenmedik bir yerden girdiği için sıranın bozulduğu düşünülebilir. append elemanı yine sona ekler; sorun kuralların atlanmasıdır.
  • E yanlış: Property ile erişim görmüş öğrenci, düz niteliğe dışarıdan erişimin hata vereceğini düşünebilir. Ama items sıradan bir örnek niteliğidir; ona erişmek hata vermez.

4. Doğru: B. Property yalnızca erişim biçimini değiştirir. Döndürülen nesne, sınıfın iç listesine giden referanstır; order.items.append(...) iç listeyi değiştirir.

  • A yanlış: Böyle bir otomatik kopyalama yoktur.
  • C yanlış: Property, döndürdüğü listeyi değiştirilemez hâle getirmez.
  • D yanlış: Alt çizgi “dokunmayın” uyarısı gibi okunduğu için bir koruma sağladığı sanılabilir. Ama alt çizgi yalnız bir adlandırma geleneğidir; teknik engel değildir.
  • E yanlış: Setter yalnızca order.items = ... gibi atamaları denetler; döndürülen listeye yapılan append çağrısını engellemez.

5. Doğru: B. Demet eleman eklemeye ve çıkarmaya izin vermez; çağıran içeriği okuyabilir ama koleksiyonu değiştiremez.

  • A yanlış: Yalnız sayıyı verir; içeriği okumaya izin vermez.
  • C yanlış: İç listeyi korur ama çağırana değiştirilebilir bir liste verir; “bu yalnızca okunur” mesajını taşımaz.
  • D yanlış: Aynı değiştirilebilir listeyi verir; dış kod append yapabilir.
  • E yanlış: copy adı kopya verildiğini düşündürür. Ama parantez olmadığı için metot çağrılmaz; çağırana listenin kendisi değil, copy metodu döner.

6. Doğru: C. Delegation’da kural kopyalanmaz; OrderLine hesabı kuralın sahibi olan ürüne devreder. İndirim kuralı değişirse yalnız Product güncellenir.

  • A yanlış: price_for() çağrısı devralınmış bir metot gibi görünebilir. Ama OrderLine bir Product nesnesini nitelik olarak tutar; aralarında kalıtım değil, kompozisyon vardır.
  • B yanlış: Hesabı başka bir metoda bırakmak bir iyileştirme gibi görünebilir. Ama araya bir çağrı daha girer; tasarımın gerekçesi hız değil, kuralın nerede durduğudur.
  • D yanlış: Fiyat Product içinde durduğu için OrderLine içinden ona ulaşılamayacağı sanılabilir. self.product.price ile ulaşılır ve kod çalışır; sorun tasarımdadır.
  • E yanlış: Örnekte hesap devredildiği için bunun zorunlu olduğu sanılabilir. Her çağrıyı devretmek gerekmez; ürüne özgü bir kural yoksa basit ara toplam OrderLine içinde de kalabilir.

7. Doğru: C. Library her nesnenin iç durumunu doğrudan yönetir ve “her şeyi yapan” sınıf olur. Book.borrow() ve Member.add_borrowed_book() gibi davranışlar kendi nesnelerine aittir; Library yalnızca bu metotları sırayla çağırmalıdır.

  • A yanlış: İşlemi Library yönettiği için bütün kuralların da orada durması gerektiği sanılabilir. Kurallar ait oldukları nesnelerde durmalıdır; işlemin sırasını yönetmek ayrı bir iştir.
  • B yanlış: Sayaç kütüphanedeki bütün ödünçleri sayıyormuş gibi okunursa ortak bir nitelik akla gelir. Ama her üyenin kendi sayısı olmalıdır; sınıf niteliği bütün üyelerin sayısını birleştirirdi.
  • D yanlış: İki farklı sınıftan nesne alan metot karışık göründüğü için sorun orada sanılabilir. Metot birden çok nesne alabilir; sorun parametrelerde değil sorumluluk dağılımındadır.
  • E yanlış: Kapsülleme konusu, dışarıdan atamanın engellendiği izlenimini bırakabilir. Python’da nitelik dışarıdan değiştirilebilir; sorun, kuralın yanlış sınıfta durmasıdır.

8. Doğru: A. Book.borrow() ve Member.add_borrowed_book() gibi metotlarla her nesne kendi durumunun kurallarını korur. Library yalnızca bu metotları sırayla çağırır ve işlemin sırasını yönetir.

  • B yanlış: Sınıf sayısı azaldığı için tasarım sadeleşmiş gibi görünür. Oysa kurallar hiçbir nesneye ait olmaz; sorumluluk dağılımı büsbütün kaybolur.
  • C yanlış: Metodu sınıftan çıkarmak Library sınıfını küçülttüğü için çözüm gibi görünür. Ama aynı merkezî kural yığını bu kez fonksiyona taşınır.
  • D yanlış: Property doğrulama için kullanıldığından burada da çözüm gibi görünür. Ama property erişimi düzenler, davranışın hangi nesneye ait olduğunu değiştirmez.
  • E yanlış: Kalıtım, kitap ve üye davranışlarını tek yerde toplamanın kısa yolu gibi görünür. Ama kütüphane bir kitap ya da üye değildir; kalıtım yanlış modeldir.

9. Doğru: A. Kullanım biçimi “numaradan nesneye erişim” ise sözlük bu işi doğrudan yapar: self._members[member_id] listeyi taramadan üyeyi verir.

  • B yanlış: İç listeyi dışarıya demet olarak vermek, demetin burada da uygun olduğunu düşündürebilir. Ama üye eklemek gerekir; demet buna izin vermez.
  • C yanlış: Liste en tanıdık koleksiyon olduğu ve sırayı koruduğu için ilk akla gelen seçimdir. Ama numarayla aramada her seferinde listeyi baştan taramak gerekir.
  • D yanlış: Üye numaraları benzersiz olduğu için küme uygun görünebilir. Küme bir değerin içinde olup olmadığını söyler; ama numarayı verip üye nesnesini alamazsınız.
  • E yanlış: Birkaç üyeyle self.member_1 gibi nitelikler işe yarıyormuş gibi görünür. Üye sayısı önceden bilinmez; her yeni üye için sınıfa nitelik yazılamaz.

10. Doğru: D. Kullanım biçimi “sırayla ekle, sırayla dolaş” olduğu için liste yeter. append() yolcuyu sona ekler, for döngüsü biniş sırasını korur. Numarayla erişim gerekmediği için sözlüğün anahtarına ihtiyaç yoktur.

  • A yanlış: Birkaç yolcuyla self.passenger_1 gibi nitelikler işe yarıyormuş gibi görünür. Ama yolcu sayısı önceden bilinmez ve bu nitelikler bir döngüyle sırayla dolaşılamaz.
  • B yanlış: Sıra korunacağı için demet uygun görünebilir. Ama yolcular sefer boyunca ekleniyor; demete eleman eklenemez.
  • C yanlış: Üyeleri numarayla bulan örneklerde sözlük seçildiği için bunun genel bir kural olduğu sanılabilir. Ama seçimi kullanım biçimi belirler; burada hiçbir yolcu anahtarla aranmıyor.
  • E yanlış: Aynı yolcunun iki kez eklenmemesi akla gelince küme uygun görünebilir. Ama küme elemanların sırasını korumaz; biniş sırasıyla yazdırma işi kümeyle yapılamaz.

Tek sayfa özet

  • Bir nesne başka bir nesneyi niteliğinde tutabilir (has-a). Python nesneyi kopyalamaz, aynı nesneye referans tutar.
  • Nesne koleksiyonu da nesnelerin referanslarını tutar. Listedeki ürünün fiyatı değişirse siparişin toplamı da değişir.
  • Bir nesnenin tuttuğu liste ya da sözlük de o nesnenin durumudur. Kapsülleme kuralları onlar için de geçerlidir.
  • İç listeyi olduğu gibi dışarı verirseniz dış kod, aliasing yüzünden sınıfın kontrollerini atlayabilir. Yalnız okunacaksa demet, çağıran düzenleyecekse kopya döndürün.
  • Delegation, bir işi o işin kuralını bilen nesneye bırakmaktır. Kural tek sınıfta kalır.
  • Bir sınıf öteki nesnelerin niteliklerini kendisi değiştirirse kurallar o sınıfta birikir. Her nesne kendi durumunu kendi metoduyla değiştirsin.
  • Bire çok ilişkide liste mi sözlük mü kullanacağınızı, koleksiyona nasıl erişeceğiniz belirler. Numarasıyla bulunacak nesneler için sözlük kullanın.
  • Başka bir sınıfın davranışından yararlanmanın tek yolu kalıtım (gelecek hafta) değildir. Kompozisyonla o sınıfın nesnesini tutup metodunu çağırabilirsiniz.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Bir nesnenin başka bir nesneyi tuttuğu has-a ilişkisini kurar.
  • Nesne koleksiyonunun nesnelerin kendisini değil referanslarını tuttuğunu belirler.
  • Değiştirilebilir iç koleksiyonu dışarı vermenin kapsüllemeyi bozduğunu gösterir.
  • İç listeyi dışarıya demet ya da kopya olarak verme seçeneklerini karşılaştırır.
  • Davranışı kuralın sahibine bırakarak delegation uygular.
  • Sorumluluğu yanlış sınıfa yığmanın o sınıfı “her şeyi yapan merkeze” dönüştürdüğünü açıklar.
  • Bire çok ilişkide liste ile sözlük seçimini kullanım biçimine göre yapar.
Başa dön