Sınıf, Nesne ve Kapsülleme — Kuralı Kim Koruyor?
30 saniyede özet
Kapsülleme alanları private yapmak değil, nesnenin kurallarını yalnızca kendi metotlarıyla değiştirilebilir kılmaktır. Kural genelde iki arka kapıdan kaçar: iç listeyi veren getter ve dışarıdaki listeyi saklayan constructor.
Siparişin toplamı 350 TL diyor, satırlar 390 TL ediyor. Kimse total alanına dokunmadı; alan zaten private. Satırlar ise başka bir kapıdan değişti.
-
Bayt: Siparişin toplamı 350 diyor, satırlar 390 ediyor!
-
Sen: Ama total alanı private. Kimse ona dokunamaz ki.
-
Bayt: Total'e dokunan yok. Peki satırlara başka bir kapıdan giren var mı?
-
Bayt: Hadi bakalım: bu nesnenin kuralını kim koruyor?
Nesne kendi kuralını korur
Bir nesne yalnızca veri tutmaz, o verinin uyması gereken kuralı da taşır. Sipariş (Order) için kural basit: toplam, her zaman satırların toplamına eşittir. Hiç bozulmaması gereken bu tür kurallara değişmez kuralNesne var olduğu sürece doğru kalması gereken şey. Kapsüllemenin ölçütü kaç alanın private olduğu değil, hangi kuralın dışarıdan bozulamadığıdır.Bir setter, koruduğu kural varsa onu atlanabilir kılar. Kuralı metodun içine koymak (withdraw gibi) nesneyi geçersiz duruma düşürmeyi imkânsız hâle getirir.Sözlükte gör → denir.
kapsüllemeBir nesnenin iç durumunu yalnızca kendi metotlarından değiştirilebilir kılmak. Amaç alanları gizlemek değil, nesnenin kuralını (invariant) korumaktır.Sözlükte gör →, veriyi yalnızca kuralı bilen metotların değiştirebilmesidir. addLine() satırı eklerken toplamı da günceller; iki iş hep birlikte yapıldığı için kural bozulamaz.
Her alana bir setter açmak kapsülleme değildir. setTotal() varsa herkes toplamı istediği sayıya çekebilir ve kuralı koruyan kimse kalmaz.
Kafam karıştı, daha basit anlat
Kasiyer düşün: sepete ürün eklerken toplamı da o günceller. Müşteri toplamı kendi eliyle değiştiremez. Kapsülleme, veriyi yalnızca kuralı bilenin değiştirmesidir.
Kapsüllemenin asıl amacı nedir?
Bir sınıfın bütün alanları private ama her biri için public getter ve setter var. Bu sınıf kapsüllenmiş midir?
final ve private yetmez
private final List<Integer> lines alanı ve getLines() { return lines; } var. Dışarıdaki kod order.getLines().add(40) yazarsa ne olur? Cevabı göster
Ekleme çalışır, toplam güncellenmez. final yalnızca referansı sabitler; getter iç listenin kendisini dışarı verdi.
Adım adım oku
- addLine ön kapıdır: satırı eklerken toplamı da günceller. Kural tamam.
- getLines ise iç listenin kendisini dışarı verir: bir arka kapı açılır.
- Dış kod bu listeye 40 ekler. Satırlar 390 olur, toplam hâlâ 350: kural bozuldu.
- Çözüm: dışarıya listenin kopyasını ya da değiştirilemez bir görünümünü ver. Dış kod ancak kendi kopyasını değiştirebilir.
Kaçak yollardan ikincisi constructor’dır. this.lines = lines yazınca Order, dışarıdan verilen listenin kendisini saklar. Listeyi veren kişi onu sonra değiştirirse, Order’ın içi de habersizce değişir.
İki kapıyı da kapat. İçeri alırken listenin bir kopyasını al (buna defensive copyDışarıdan alınan ya da dışarıya verilen değiştirilebilir bir nesnenin kopyasını kullanmak; böylece başkasının referansı iç durumu değiştiremez.Sözlükte gör → denir), dışarı verirken de değiştirilemeyen bir liste ver.
Aşağıdaki örnek bir bankanın vadesiz hesabı. Kural iki parçalı: bakiye her zaman hareketlerin toplamına eşittir ve ek hesap limitinin altına inmez. İki alanı da değiştiren tek bir kapı var.
Derinleş · Vadesiz hesap: kuralı tek kapıdan korumak 4 dosya · ~128 satır · ilk okumada atlayabilirsin
Kafam karıştı, daha basit anlat
Listenin kendisini dışarı verirsen, alan kişi içine kasiyere sormadan ürün ekleyebilir. Dışarıya kopya ya da salt okunur bir görünüm ver.
`private final List<Line> lines;` ve `public List<Line> getLines() { return lines; }`. Alan private ve final. Dışarıdaki kod listeyi değiştirebilir mi?
Her getter'ı, dışarıdaki kodun iç durumu değiştirmesine izin verip vermediğine göre ayır.
Kendin gör
Kapsülleme — invariant hangi kapıdan bozulur?
Tohum 305269class Order { private final List<Integer> lines; private int total; Order(List<Integer> lines) { this.lines = lines; this.total = sum(lines); } void addLine(int amount) { lines.add(amount); total += amount; } List<Integer> getLines() { return lines; }} var amounts = new ArrayList<>(List.of(100, 250));var order = new Order(amounts);order.addLine(40);- Oynat ya da adımla.
Şu an ne oldu?
Order: satırlar ve önbelleğe alınmış bir toplam
Kural: total her zaman satırların toplamına eşit. addLine() bu kuralı korur. Başka bir kapı var mı?
Görevler0/3
Invariant'ı getter üzerinden bozaçık
İpucu
Getter iç listenin kendisini veriyorsa, dışarıdaki kod ona ne yapabilir?
getLines() unmodifiableList dönerken bile invariant'ı bozaçık
İpucu
unmodifiableList bir kopya değil, bir görünüm. Altındaki listeye başka kimin referansı var?
Dışarıdan bir değişiklik girişimine rağmen invariant sağlam kalsınaçık
İpucu
İki kapıyı da kapat: içeri alırken kopyala, dışarı verirken koru.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. addLine: kural sağlam.
- Çağıranı “getLines().add” yap. Kural bozuldu, hata yok.
- Getter’ı unmodifiableList yap. Ekleme reddedildi.
- Çağıranı “verdiği listeye ekliyor” yap. Getter korumalı, kural yine bozuk.
- Constructor’ı “kopyalıyor” yap. Kural sağlam.
`getLines()` artık `Collections.unmodifiableList(lines)` dönüyor, ama constructor `this.lines = lines;` ile çağıranın listesini saklıyor. Çağıran kendi listesine eleman eklerse ne olur?
Satır satır: görünüm ve kopya
Aynı liste, iki farklı bakış
List<String> tags = new ArrayList<>(List.of("a"));List<String> view = Collections.unmodifiableList(tags);List<String> copy = List.copyOf(tags); tags.add("b");System.out.println(view.size() + " " + copy.size());Debug
main Değiştirilebilir asıl liste.
- tags
- = [a]
Sol/sağ ok tuşlarıyla da gezebilirsin.
Bir unmodifiable viewCollections.unmodifiableList gibi, yazmayı reddeden ama altındaki koleksiyonun değişikliklerini yansıtan salt okunur bir görünüm. Kopya değildir.Sözlükte gör → yalnızca kendi üzerinden yazmayı engeller. Altındaki listeye başka biri yazabiliyorsa, koruma yoktur.
Kafam karıştı, daha basit anlat
Salt okunur görünüm, vitrin camı gibidir: sen dokunamazsın ama arkadaki biri rafı değiştirebilir. Kopya ise sana ait ayrı bir raftır.
Program ne yazdırır?
Tuzaklar
Değiştirilebilen tipler. java.util.Date ya da bir dizi dışarı verilirse, alan kişi içini değiştirebilir. Bunların yerine değiştirilemeyen Instant ve List.copyOf kullan.
Record da listeyi korumaz. record Order(List<Line> lines) yalnızca hangi listeyi gösterdiğini sabitler, listenin içini değil. Listeyi record’un kurucusunda kopyala.
Önce kopyala, sonra kontrol et· istersen atla
Listeyi önce kontrol edip sonra kopyalarsan, bu ikisinin arasında başka bir thread asıl listeyi değiştirebilir. Güvenli sıra tersidir: önce kopyala, sonra kopyayı kontrol et.
Sessiz kopya. Getter her seferinde bir kopya dönerse, ona eklenen eleman hiçbir yere gitmez ve kimse fark etmez. Salt okunur bir liste ise ekleme denendiği anda hata verir.
Rezervasyonun bitiş tarihi, oluşturulduktan sonra kimse setEnd çağırmadığı hâlde değişiyor. Hangi satırlar?
Kendini sına
Her alana getter ve setter açmak kapsülleme demektir.
Constructor'da doğrulama ve savunmacı kopya birlikte yapılacak. Sıra neden önemli?
Aklında kalacak üç şey
- 1 Kapsüllemenin amacı nesnenin kuralını korumaktır. Her alana setter açmak, alanı herkese açmaktan pek farklı değildir.
- 2 final bir değişkeni sabitler, gösterdiği nesneyi değil. İç listeyi olduğu gibi döndüren bir getter, private final bir alanı herkese açar.
- 3 unmodifiableList bir kopya değil, bir penceredir. Kuralı korumak için içeri alırken kopyalamak, dışarı verirken de korumak gerekir.
3 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.
- Nesne Yönelimli ProgramlamaErişim Belirleyiciler — Kim Hangi Kapıdan Girer?Sınıftaki alanı private yaptın ama equals metodu başka bir nesnenin private alanını okuyabiliyor. Gizlilik bu mu?Derse git
- Nesne Yönelimli ProgramlamaKalıtım ve Kompozisyon — Kimin İç Ayrıntısına Bağlısın?Üst sınıfta tek bir satır değişti ve alt sınıfın sayacı ikiye katlandı. Nasıl olur?Derse git