SEN2001 · 10. hafta · Abstract Data Types

Soyut veri tipleri (ADT) ve kapsülleme

Soyutlama kavramı, ADT'nin iki koşulu, information hiding, Ada paketleri, C++ ve Java sınıfları, parametreli ADT'ler (generics) ve büyük programlar için kapsülleme yapıları (namespace, package).

abstractionprocess abstractiondata abstractionabstract data typeinformation hidingencapsulationinterfaceimplementationaccessorconstructordestructorAda packagespecification packagebody packageprivate typelimited privateC++ classaccess modifierfriendparameterized ADTgeneric classtemplatenamespaceJava package

Bir programın karmaşıklığıyla başa çıkmanın yolu soyutlama (abstraction): bir varlığın sadece o an önemli olan özelliklerini görmek, gerisini gizlemek. Alt programlar process abstraction’dır (sıralama algoritmasını bilmeden sort çağırırsın). Bu hafta ikincisi: data abstraction, yani abstract data type (ADT). Sınavda kavramsal sorular (ADT’nin iki koşulu, information hiding’in faydası, dil karşılaştırmaları) ve bir ADT’yi kullanan kodun çıktısı sorulur.

Altın kurallar:

  1. ADT = gizli temsil + tek birimde tanımlanmış tip ve işlemler.
  2. Kullanıcı sadece arayüzü görür. İçeriyi değiştirmek kullanıcı kodunu bozmaz.
  3. Yığın LIFO, kuyruk FIFO. Çıktı sorularında içeriği her adımda yaz.
  4. Parametreli ADT tip hatalarını derlemede yakalar.

Abstract data type

Kullanıcı tanımlı bir tip, iki koşulu sağlıyorsa ADT’dir:

  1. Temsil gizli: tipin nesnelerinin iç yapısı, tipi kullanan birimlerden gizlidir. Kullanıcı nesnelere sadece tipin tanımladığı işlemlerle erişir.
  2. Tek birim: tipin bildirimi ve üzerindeki işlemlerin protokolleri tek bir sözdizimsel birimde yer alır. Diğer birimler bu tipten değişken yaratabilir.

Aslında float da bir ADT gibi: bitlerin nasıl dizildiğini bilmeden + ve * kullanırsın. Kullanıcı tanımlı ADT’ler aynı rahatlığı programcının tiplerine getirir.

Information hiding’in faydaları:

  • Reliability: kullanıcı nesnenin iç durumunu bozamaz (yığının top alanını rastgele bir değere çekemez).
  • Değiştirilebilirlik: temsil değişirse (dizi yerine bağlı liste) kullanıcı kodu değişmez, çünkü sadece arayüze bağlı.
  • Daha az isim çakışması ve daha küçük, yönetilebilir birimler.

Java’da ADT: sınıf

static class IntStack {
    private int[] items = new int[10];
    private int top = -1;
    public void push(int x) { items[++top] = x; }
    public int pop() { return items[top--]; }
    public int peek() { return items[top]; }
    public boolean isEmpty() { return top == -1; }
}

IntStack s = new IntStack();
s.push(3); s.push(7); s.pop(); s.push(2); s.push(9);
System.out.println(s.pop() + s.peek());
11
işlem      yığın (alt → üst)
push 3     [3]
push 7     [3, 7]
pop        [3]          (7 döndü)
push 2     [3, 2]
push 9     [3, 2, 9]
pop        [3, 2]       (9 döndü)
peek                    (2, silinmez)  → 9 + 2 = 11

Dışarıdan s.top = 5; ya da a.balance = 5000; yazmak derleme hatası verir:

error: balance has private access in Account

Java’da erişim düzeyleri: private (sadece sınıf), paket erişimi (belirtilmezse: aynı paket), protected (paket + alt sınıflar), public. Erişim sınıf düzeyindedir: bir Money nesnesi başka bir Money nesnesinin private alanını okuyabilir.

Immutable ADT

static final class Money {
    private final int cents;
    Money(int c) { cents = c; }
    Money add(Money o) { return new Money(cents + o.cents); }
    int cents() { return cents; }
}
Money m1 = new Money(150);
Money m2 = m1.add(new Money(75));
System.out.println(m1.cents() + " " + m2.cents());
150 225

Değişmeyen nesneler paylaşılabilir; aliasing sorunları (bir yerden değiştirilip başka yerden şaşırma) ortadan kalkar. Java’nın String’i de böyle.

Arayüz ile temsil ayrı

List<Integer> nums = new ArrayList<>(List.of(4, 1, 3));
List<Integer> view = Collections.unmodifiableList(nums);
try { view.add(5); } catch (UnsupportedOperationException e) { System.out.println("UnsupportedOperationException"); }
nums.add(5);
System.out.println(view.size());
UnsupportedOperationException
4

Diğer dillerde ADT

DilYapıGizlemeNot
Adapackage: specification + bodyspec’in private kısmıprivate tip: :=, = var; limited private: yok
C++class (ve struct)private, protected, publicclass varsayılan private, struct public; friend erişim verir; constructor/destructor
Javaclasserişim belirleyicileriTüm nesneler heap’te, destructor yok (garbage collection)
C#class, structJava’ya ek olarak internalproperty: get/set ile alan gibi görünen accessor
Rubyclassmetotlar varsayılan public, alanlar her zaman gizliattr_reader accessor üretir

Ada’da specification package’ın private kısmı derleyicinin görmesi için oradadır (nesnenin boyutunu bilmesi gerekir) ama kullanıcı oradaki alanlara erişemez. Temsili tamamen gizlemek için tip bir pointer olarak bildirilir, gösterdiği yapı body’de tanımlanır.

C++‘ta destructor nesnenin ömrü bitince otomatik çağrılır ve heap’te ayırdığı belleği geri verir. Yaratılışın tersi sırasıyla çalışırlar. Java’da benzer bir sıra try-with-resources’ta görülür:

try (Res r1 = new Res("A"); Res r2 = new Res("B")) {
    System.out.print("body ");
}
open A open B body close B close A

Parametreli ADT’ler

Eleman tipi ne olursa olsun aynı işi yapan bir yığın yazmak istiyorsun. Parameterized ADT (generic class) tip parametresi alır:

DilSözdizimi
Adageneric type Element is private; package Stack is ...
C++template <class T> class Stack { ... };
Java 5+class Stack<T> { ... } (sadece referans tipleri; int değil Integer)
C# 2005+class Stack<T>
List<Integer> xs = new ArrayList<>();
xs.add("five");
error: no suitable method found for add(String)

C++ template’leri her farklı tip için ayrı kod üretir (derlemede örnekleme). Java generics tek bir kod kullanır ve tip bilgisi derlemeden sonra silinir (type erasure); bu yüzden new T() yazılamaz.

Büyük programlar için kapsülleme

ADT tek bir tipi kapsüller. Binlerce sınıflı bir sistemde ilgili tipleri ve alt programları gruplamak, ayrı derlemek ve isim çakışmalarını önlemek gerekir.

  • C: başlık dosyası (.h, arayüz) + uygulama dosyası (.c). Derleyici iki dosyanın tutarlılığını garanti etmez.
  • C++, C#: namespace. std::cout gibi nitelikli isimler ya da using.
  • Java: package. Paket hem isim alanı hem erişim birimidir: belirleyicisi olmayan üyeler paket içinde görünür.
  • Ada: package’lar iç içe ve ayrı derlenebilir.
  • Ruby: module.

Sık yapılan hatalar

YanlışDoğrusu
ADT = herhangi bir structTemsil gizli olmalı
Gizleme sadece güvenlik içinAsıl kazanç: temsil değişince kullanıcı kodu değişmez
peek() elemanı silerSadece okur
Kaynaklar açılış sırasıyla kapanırTers sırayla
C++ class varsayılan publicprivate; struct public
Java’da private = nesneye özelSınıfa özel; aynı sınıfın başka nesnesi erişir
Ada private tipte atama yokVar; limited private’ta yok
List<int> Java’da geçerliList<Integer>; generic parametre referans tipi

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

State the two conditions that a user-defined type must satisfy to be an abstract data type.

Cevabı göster
the representation is hidden from users; the type and its operations are declared in a single syntactic unit

(1) Information hiding: tipin nesnelerinin temsili (iç yapısı) onu kullanan birimlerden gizlidir, kullanıcı sadece tanımlı işlemlerle erişir. (2) Tipin bildirimi ve işlemlerinin protokolleri tek bir sözdizimsel birimde (class, package) toplanır; diğer birimler bu tipten değişken yaratabilir.

Soru 2kolay

What is printed in Java?

IntStack s = new IntStack();   // push, pop, peek on an int array
s.push(3); s.push(7); s.pop(); s.push(2); s.push(9);
System.out.println(s.pop() + s.peek());
Cevabı göster
11

Yığın LIFO: [3] → [3, 7] → pop 7 → [3] → [3, 2] → [3, 2, 9]. pop() 9 döner, yığın [3, 2]. peek() 2 (silmez). 9 + 2 = 11.

Soru 3orta

What is printed in Java?

IntStack t = new IntStack();
for (int i = 1; i <= 4; i++) t.push(i * i);
int sum = 0;
while (!t.isEmpty()) { int v = t.pop(); if (v % 2 == 0) sum += v; }
System.out.println(sum);
Cevabı göster
20

Yığına 1, 4, 9, 16 girer. Ters sırayla çıkar: 16, 9, 4, 1. Çiftler 16 + 4 = 20. Kullanıcı kod içerideki diziyi hiç görmedi; sadece push, pop, isEmpty kullandı.

Soru 4orta

What is printed? Queue2<T> wraps a LinkedList with enqueue (add at end), dequeue (remove from front) and size.

Queue2<String> q = new Queue2<>();
q.enqueue("a"); q.enqueue("b"); q.dequeue(); q.enqueue("c");
System.out.println(q.dequeue() + q.size());
Cevabı göster
b1

Kuyruk FIFO: [a, b] → dequeue a → [b] → [b, c] → dequeue b, kalan [c]: size 1. Sonuç "b" + 1 = "b1". Queue2 parametreli (generic) bir ADT: aynı kod her eleman tipiyle çalışır.

Soru 5kolay

Does this Java code compile? Why?

class Account {
    private int balance = 100;
    public int getBalance() { return balance; }
}
// in another class:
Account a = new Account();
a.balance = 5000;
Cevabı göster
No: balance has private access in Account

private üyeye sadece sınıfın içinden erişilir. Dışarıdan doğrudan değiştirme engellenir: information hiding. Hesap bakiyesinin geçerli kalması (negatif olmaması gibi) sınıfın kendi metotlarıyla garanti edilebilir. Okumak için accessor (getBalance) kullanılır.

Soru 6orta

In what order are the messages printed?

// Res(String n) prints "open n", close() prints "close n"
try (Res r1 = new Res("A"); Res r2 = new Res("B")) {
    System.out.print("body ");
}
Cevabı göster
open A open B body close B close A

Kaynaklar yazıldığı sırayla açılır, blok bitince ters sırayla kapanır. Bu, C++'ta destructor'ların yapıcıların tersi sırasıyla çağrılmasına benzer: sonra yaratılan, önce yaratılana bağlı olabilir, o yüzden önce o temizlenir.

Soru 7orta

Does this compile? Which ADT feature does it illustrate?

List<Integer> xs = new ArrayList<>();
xs.add("five");
Cevabı göster
No; parameterized (generic) ADTs are type checked at compile time

List<Integer> parametreli bir ADT'nin Integer ile örneklenmiş halidir. String eklemek derleme zamanında yakalanır: "no suitable method found for add(String)". Java 5'ten önceki ham koleksiyonlar Object tutardı ve hata ancak çalışırken, cast sırasında çıkardı.

Soru 8kolay

In C++, what is the default access of members of a class and of a struct?

Cevabı göster
class: private; struct: public

C++'ta class ile struct arasındaki tek fark varsayılan erişim. Üç erişim düzeyi: private (sadece sınıf ve friend'ler), protected (ayrıca alt sınıflar), public (herkes). Java'da belirtilmezse package erişimi (aynı paketteki sınıflar) geçerli.

Soru 9zor

In Ada, what is the difference between a private type and a limited private type declared in a package specification?

Cevabı göster
private: users get assignment and equality; limited private: no built-in assignment or equality

Ada'da ADT bir package ile tanımlanır: specification package arayüzü, body package uygulamayı tutar. private tipte kullanıcı :=, = ve /= kullanabilir ama iç yapıya erişemez. limited private'ta bunlar da yoktur; sadece paketin verdiği işlemler kullanılabilir. Pointer içeren tipler için kopyalamanın anlamını paket belirlemeli.

Soru 10orta

What is printed in Java?

static final class Money {
    private final int cents;
    Money(int c) { cents = c; }
    Money add(Money o) { return new Money(cents + o.cents); }
    int cents() { return cents; }
}
// in main:
Money m1 = new Money(150);
Money m2 = m1.add(new Money(75));
System.out.println(m1.cents() + " " + m2.cents());
Cevabı göster
150 225

Money immutable bir ADT: alan final ve private, add yeni bir nesne döndürür, m1'i değiştirmez. Dikkat: o.cents başka bir Money nesnesinin private alanı ama aynı sınıfın içinden erişildiği için geçerli. Java'da erişim sınıf düzeyindedir, nesne düzeyinde değil.

Soru 11zor

What is printed in Java?

List<Integer> nums = new ArrayList<>(List.of(4, 1, 3));
List<Integer> view = Collections.unmodifiableList(nums);
try { view.add(5); }
catch (UnsupportedOperationException e) { System.out.println("UnsupportedOperationException"); }
nums.add(5);
System.out.println(view.size());
Cevabı göster
UnsupportedOperationException then 4

view, aynı listeye sadece okuma işlemleri sunan bir arayüzdür; değiştirme işlemleri istisna fırlatır. Ama veri kopyalanmadı: nums değişince view da değişir (4 eleman). Arayüz ile temsilin ayrılması tam olarak bu.

Soru 12kolay

Name the encapsulation construct for large programs: (a) in C++ and C#; (b) in Java; (c) in Ada.

Cevabı göster
(a) namespaces (b) packages (c) packages

Büyük programlarda isim çakışmalarını önlemek ve ilgili kodu birlikte derlemek için ADT'den büyük kapsüller gerekir. C++ namespace ve using, C# namespace, Java package ve import (paket erişimi de verir), Ada package'ları (iç içe olabilir). C'de bu iş başlık dosyaları (.h) ve ayrı derlenen .c dosyalarıyla yapılır.

Doğrulama: Yığın/kuyruk ADT çıktıları, try-with-resources kapanış sırası, immutable sınıf ve unmodifiable view örnekleri JDK ile çalıştırıldı (java verify/SEN2001/W10.java). private erişim ve generic tip hatası örnekleri javac ile derlenmeye çalışıldı ve beklenen derleme hatalarını verdi (verify/SEN2001/w10b/W10b.java, W10c.java). C++ ve Ada örnekleri kavramsal; derleyicileri kurulu değil.

Deftere not kartı

abstraction: sadece önemli özellikleri göster. process (alt program) + data (ADT) ADT koşulları: (1) temsil GİZLİ (2) tip + işlemler TEK birimde information hiding avantajları: reliability, değişiklik kolay, isim çakışması az arayüz (interface) ≠ uygulama (implementation) accessor: get/set ile kontrollü erişim. constructor yaratır, destructor (C++) temizler destructor/kapanış sırası yaratılışın TERSİ (try-with-resources: B sonra A) Ada: specification package + body package. private: := ve = var; limited private: yok C++: class varsayılan private, struct public. friend erişim verir Java: private, protected, public, (yok) = package. erişim SINIF düzeyinde C#: ayrıca property (get/set sözdizimi), struct = değer tipi parameterized ADT: Java generics, C++ template, Ada generic package List<Integer> add("five") → DERLEME hatası büyük kapsül: C++/C# namespace, Java package, Ada package, C .h + .c