Kompozisyon, delegation ve nesne koleksiyonları

Gerçek uygulamalar tek bir sınıftan oluşmaz. Bir sipariş müşteriye ve ürünlere, bir kütüphane kitaplara ve üyelere, bir takım oyunculara sahiptir. Bu hafta nesneler arasındaki has-a ilişkisini, kompozisyonu ve bir nesnenin başka nesnelere işi devretmesini (delegation) kullanacağız.

Bu hafta neleri yapabilmelisiniz?

  • has-a ilişkisinin anlamını açıklayabilmeli,
  • Bir sınıfın başka sınıftan nesneyi nitelik olarak tutmasını tasarlayabilmeli,
  • Nesne koleksiyonlarını liste veya sözlükte yönetebilmeli,
  • İç koleksiyonu doğrudan dışarı açmanın kapsüllemeyi nasıl bozabileceğini açıklayabilmeli,
  • Delegation ile sorumluluğu doğru nesneye aktarabilmeli,
  • Kimlik/tekrar kontrolünü nesne koleksiyonlarında uygulayabilmeli,
  • Kompozisyonun kalıtıma alternatif olabileceğini sezgisel olarak açıklayabilmelisiniz.

Bir nesne başka nesneyi tutabilir

Order, bir Customer nesnesine sahiptir: Order has a Customer.

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

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

Bu derste kompozisyon sözcüğünü, nesneleri içererek/kullanarak davranış oluşturma biçimindeki geniş has-a yaklaşımı için kullanıyoruz. UML ve bazı kaynaklarda aggregation (agregasyon) ile composition (bileşim), parçanın yaşam döngüsünün bütüne ne ölçüde bağlı olduğuna göre ayrıca ayrılır. Bu ayrımı tanımanız yararlıdır; ancak bu hafta uygulama ve ölçme beklentimiz formal UML ayrımı değil, doğru nesne ilişkisi ve sorumluluk tasarımıdır.

Nesne koleksiyonu

_items, sayı listesi değil Product nesnelerine ait referansları tutar.

Kritik sentez: iç koleksiyonu dışarı açmak kapsüllemeyi delebilir

Şu tasarım ilk bakışta kullanışlı görünür:

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

    def add_product(self, product):
        # burada tekrar, stok veya başka kurallar olabilir
        self.items.append(product)

Ama çağıran kod şunu yapabilir:

order.items.append(product)

Böylece add_product() içindeki bütün kontroller atlanır. Daha da önemlisi, items listesini doğrudan döndüren bir property de aynı liste nesnesine referans verirse sorun çözülmüş olmaz:

@property
def items(self):
    return self._items  # dış kod yine append yapabilir

Dışarıya yalnızca okunabilir bir görünüm vermek istiyorsak örneğin:

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

veya bağımsız sığ kopya gerekiyorsa:

return self._items.copy()

kullanılabilir. Hangi yaklaşımın uygun olduğu arayüz sözleşmesine bağlıdır.

Important3. hafta + 8. hafta burada birleşiyor

Bu sorun iki önceki fikrin doğrudan birleşimidir: 3. haftada mutable nesnelerde aliasing gördük; 8. haftada durum değişimini kontrollü bir arayüzden geçirmek gerektiğini öğrendik. İç listeyi dışarı verdiğiniz anda dış kod aynı mutable nesneye referans alır ve sınıfın kontrol kapısını dolaşabilir.

Delegation: işi sahibine bırakmak

OrderLine, ürünün fiyatlandırma kuralını kopyalamaz; Product.price_for() davranışına delegasyon yapar.

ImportantDelegation mekanik bir kural değildir

Amaç “her çağrıyı başka nesneye devretmek” değil, davranışı kuralın gerçek sahibine yerleştirmektir. Basit ara toplamın OrderLine içinde kalması da bağlama göre makul olabilir.

Sorumluluk yanlış yerdeyse ne olur?

class Library:
    def borrow_book(self, member, book):
        book.is_borrowed = True
        member.borrowed_count += 1
        # onlarca kural...

Library her nesnenin iç durumunu doğrudan yönetirse hızla “her şeyi yapan” merkeze dönüşür. Book.borrow() ve Member.add_borrowed_book() gibi davranışlar kendi nesnelerine ait olabilir; Library işlemi koordine eder.

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

Nesne koleksiyonu

Çokluk ilişkisi ve koleksiyon seçimi

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

Bir-çok ilişkide liste veya sözlük seçimi kullanım biçimine göre yapılmalıdır. Üye numarasından doğrudan erişim gerekiyorsa sözlük doğal olabilir:

Tekrar kontrolü koleksiyonu yöneten sınıfın sorumluluğundadır.

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

Car–Engine, Order–Customer, Playlist–Song, Student–Course çiftlerinde ilişkinin has-a olup olmadığını ve hangi tarafın diğerini tutmasının makul olduğunu tartışın. Her ilişkiyi iki tarafta da saklamanın senkronizasyon maliyetini ayrıca düşünün.

Alıştırma — Nesne koleksiyonunu tamamla

self._players.append(player)

Küçük üretim görevi — Kütüphane modeli

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

  • Kitap ISBN ile tanımlansın,
  • Aynı ISBN iki kez eklenemesin,
  • Üye numarası benzersiz olsun,
  • Library kitap ve üyeleri koleksiyonlarda tutsun,
  • Koleksiyonların mutable iç nesnelerini doğrudan dışarı vermesin,
  • Ödünç alma davranışının hangi sınıfa/sınıflara ait olduğuna gerekçeyle karar verin.

Çalışır kod teslimi: İki kitap ve iki üye ekleyin; tekrarlı ISBN ve üye numarası durumlarının hata verdiğini assert/try-except ile doğrulayın. Ayrıca dışarıya verdiğiniz koleksiyon görünümünün append() ile değiştirilemediğini veya değişikliğin iç koleksiyonu etkilemediğini gösterin.

Kendinizi kontrol edin

  1. has-a ilişkisi neyi ifade eder?
  2. Bu derste kompozisyonu hangi geniş anlamda kullanıyoruz; formal agregasyon ayrımı nerede duruyor?
  3. İç listeyi doğrudan döndürmek neden kapsüllemeyi bozabilir?
  4. tuple(self._items) ile self._items.copy() hangi ortak problemi çözer?
  5. Delegation nedir?
  6. Neden her ilişkiyi iki yönlü tutmak gerekmez?
  7. Kompozisyon kalıtımdan hangi açıdan daha gevşek bağ kurabilir?

Bu haftadan akılda kalması gerekenler

  • Gerçek sistemler nesnelerin işbirliğiyle kurulur.
  • Nesne koleksiyonları da nesnenin durumudur; kapsülleme ilkeleri onlara da uygulanır.
  • Mutable iç koleksiyonu dışarı açmak aliasing yoluyla invariant’ları deldirebilir.
  • Delegation, işi uygun sorumluluğa devretmektir.
  • Bir-çok ilişkilerde koleksiyon seçimi ve benzersizlik anahtarı kullanım ihtiyacına göre belirlenir.
  • Kalıtım tek yeniden kullanım mekanizması değildir.
Back to top