SEN1002 · 7. hafta · Case Study: Object Oriented Design with the UML

UML ile nesne yönelimli tasarım

Sınıf diyagramı okumak ve yazmak: görünürlük işaretleri, ilişki okları, çokluk, aggregation ve composition farkı, gereksinim metninden sınıflara geçiş.

UMLclass diagramvisibilityassociationmultiplicityaggregationcompositiongeneralizationrealizationdependencyrequirements documentnoun extraction

UML (Unified Modeling Language), yazılımı kod yazmadan önce çizmenin standart dilidir. Bu derste UML’in en çok kullanılan parçası olan sınıf diyagramını (class diagram) öğreniyorsun. Sınavda iki yönde gelir: “bu diyagramı Java’ya çevir” ve “bu gereksinimden sınıf diyagramı çıkar”. Okları ve işaretleri karıştırmazsan kolay puandır.

Altın kurallar:

  1. Görünürlük işaretleri: + public, - private, # protected, ~ package.
  2. UML’de tip sonda yazılır: - name : String → private String name;.
  3. Boş üçgen “is-a”: düz çizgi extends, kesikli çizgi implements.
  4. Elmas “has-a”: boş elmas aggregation, dolu elmas composition. Elmas bütün tarafında.
  5. Çokluğun “çok” tarafı koleksiyon alanı olur.

Sınıf kutusu

Bir sınıf üç bölmeli bir dikdörtgendir: en üstte ad, ortada alanlar (attributes), altta işlemler (operations).

+----------------------------------------------+
|                   Account                    |   ad (abstract ise italik)
+----------------------------------------------+
| - owner : String                             |   alanlar
| - balance : double = 0.0                     |
| - count : int                  (altı çizili) |   static
+----------------------------------------------+
| + Account(owner : String)                    |   constructor
| + deposit(amount : double) : void            |   işlemler
| + getBalance() : double                      |
| + getCount() : int             (altı çizili) |   static
| # audit() : boolean                          |   protected
+----------------------------------------------+
UMLJava
- balance : double = 0.0private double balance = 0.0;
+ deposit(amount : double) : voidpublic void deposit(double amount)
# audit() : booleanprotected boolean audit()
~ setAvailable(a : boolean)void setAvailable(boolean a) (belirleyici yok)
Altı çizili üyestatic
İtalik ad ya da {abstract}abstract class / abstract metot
<<interface>>interface
{readOnly}final alan
- items : Item [0..*]private List<Item> items; ya da dizi

TUZAK · tipin yeri. UML’de getBalance() : double, Java’da double getBalance(). Çeviri sorusunda tipleri yer değiştirmeyi unutan, imzayı bozuk yazar.

İlişkiler

Sınıflar arasındaki çizgiler ve uçlarındaki şekiller ilişkinin türünü söyler.

İlişkiÇizimAnlamıJava karşılığı
Generalization (kalıtım)Düz çizgi, boş üçgen üst sınıftais-aclass Dog extends Animal
RealizationKesikli çizgi, boş üçgen arayüzdecan-doclass Invoice implements Payable
AssociationDüz çizgi (ok varsa yön)bilir / tutarAlan: private Member member;
AggregationDüz çizgi, boş elmas bütündehas-a, parça bağımsızDışarıdan gelen nesne listede tutulur
CompositionDüz çizgi, dolu elmas bütündehas-a, parça bütüne aitParçayı bütün kendisi yaratır
DependencyKesikli okgeçici kullanırParametre ya da yerel değişken
         Person (italik: abstract)            <<interface>>
            ^                                    Payable
            |  düz çizgi + boş üçgen               ^
            |                                      :  kesikli + boş üçgen
          Staff ...................................:

   Library ◆---------- 0..*  Loan          (composition)
   Team    ◇---------- 0..*  Player        (aggregation)
   Member  1 ---------- 0..3 Loan          (association + çokluk)
   Order - - - - - - - -> Printer          (dependency)
interface Payable { double amount(); }

abstract class Person {
    protected String name;
    Person(String n) { name = n; }
    abstract String role();
}
class Staff extends Person implements Payable {
    private final double salary;
    Staff(String n, double s) { super(n); salary = s; }
    String role() { return "staff"; }
    public double amount() { return salary; }
}

Person s = new Staff("Ayse", 4200);
System.out.println(s.role() + " " + ((Payable) s).amount() + " " + (s instanceof Payable));
staff 4200.0 true

Aggregation ve composition

İkisi de “parça-bütün” (has-a) ilişkisidir. Fark parçanın ömrü:

  • Composition (dolu elmas): parça bütünle birlikte doğar ve ölür, başka bütünle paylaşılmaz. Ev ve odaları, araba ve motoru. Kodda genelde bütün, parçayı kendi constructor’ında new ile yaratır ve dışarı vermez.
  • Aggregation (boş elmas): parça bağımsız yaşar, birden fazla bütüne ait olabilir. Takım ve oyuncular, ders ve öğrenciler. Kodda parça dışarıdan gelir (add(Player p)).
class Engine { final int hp; Engine(int hp) { this.hp = hp; } }
class Car {
    private final Engine engine;                 // composition
    Car(int hp) { engine = new Engine(hp); }
    int power() { return engine.hp; }
}

class Player { final String name; Player(String n) { name = n; } }
class Team {
    private final List<Player> players = new ArrayList<>();   // aggregation
    void add(Player p) { players.add(p); }
    int size() { return players.size(); }
}

Car car = new Car(120);
System.out.println(car.power());
Player p = new Player("Arda");
Team t1 = new Team(), t2 = new Team();
t1.add(p); t2.add(p);         // aynı oyuncu iki takımda
t1 = null;                    // takım gitti, oyuncu duruyor
System.out.println(t2.size() + " " + p.name);
120
1 Arda

İPUCU · emin değilsen association. Hocalar aggregation ile düz association arasındaki farkı çoğu zaman kesin sormaz. Composition’ı ise ister: “parça bütünsüz var olabilir mi?” sorusuna “hayır” diyorsan dolu elmas.

Çokluk (multiplicity)

Çizginin uçlarına yazılan sayılar, karşı taraftaki bir nesnenin bu taraftan kaç nesneyle ilişkili olduğunu söyler.

YazımAnlamıKodda
1tam birReferans alanı, null olmamalı
0..1yok ya da birReferans alanı, null olabilir
0..* ya da *sıfır ya da çokList<T> alanı
1..*en az birList<T>, boş bırakılmaz
0..3en fazla üçList<T> + size() < 3 kontrolü

Member 1 ---- 0..3 Loan şöyle okunur: bir Member’ın 0 ile 3 arası Loan’ı var; her Loan’ın tam 1 Member’ı var.

Gereksinimden sınıflara: bir kütüphane örneği

Gereksinim metni (requirements document):

Kampüs kütüphanesinde kitaplar ve üyeler vardır. Her kitabın ISBN’i ve adı bulunur. Bir üye aynı anda en fazla üç kitap ödünç alabilir. Ödünç verilen kitap iade edilene kadar başkasına verilemez. Kütüphane, her ödünç işleminin hangi üyeye, hangi kitap için, kaç günlüğüne yapıldığını kaydeder.

Adım 1: isimleri ayıkla (noun extraction). kütüphane, kitap, üye, ISBN, ad, ödünç işlemi, gün. Kendi verisi ve davranışı olanlar sınıf: Library, Book, Member, Loan. Bir şeyin özelliği olanlar alan: isbn, title, days.

Adım 2: fiilleri ayıkla. ödünç almak, iade etmek, kaydetmek → lend, giveBack. “ödünç verilemez” ve “en fazla üç” kuralları bu metotların içindeki kontroller olur.

Adım 3: ilişkileri ve çokluğu çiz.

 Library ◇------------ 0..*  Book
 Library ◆------------ 0..*  Loan
 Member  1 ----------- 0..3  Loan
 Loan    0..1 -------- 1     Book     (bir kitabın aynı anda en fazla 1 aktif ödüncü)

Adım 4: koda çevir.

class Loan {
    private final Book book;
    private final Member member;
    private final int days;
    Loan(Book b, Member m, int days) { book = b; member = m; this.days = days; }
    Book getBook() { return book; }
    Member getMember() { return member; }
    public String toString() { return member.getName() + " -> " + book + " for " + days + " days"; }
}

class Library {
    private final List<Book> books = new ArrayList<>();
    private final List<Loan> loans = new ArrayList<>();

    void addBook(Book b) { books.add(b); }

    Loan lend(Book b, Member m, int days) {
        if (!b.isAvailable()) { System.out.println("Not available: " + b); return null; }
        if (!m.canBorrow())   { System.out.println("Limit reached: " + m.getName()); return null; }
        Loan l = new Loan(b, m, days);          // Loan'ı Library yaratır: composition
        b.setAvailable(false);
        m.addLoan(l);
        loans.add(l);
        return l;
    }
    void giveBack(Loan l) {
        l.getBook().setAvailable(true);
        l.getMember().removeLoan(l);
        loans.remove(l);
    }
    int activeLoans() { return loans.size(); }
}

Member içinde List<Loan> loans ve canBorrow() → loans.size() < 3 var. Senaryo:

Loan l1 = lib.lend(b1, ece, 14);
System.out.println(l1);
lib.lend(b1, mert, 7);      // b1 ödünçte
lib.lend(b2, ece, 7);
lib.lend(b3, ece, 7);
lib.lend(b4, ece, 7);       // Ece'nin 3 ödüncü var
System.out.println(ece.loanCount() + " " + lib.activeLoans());
lib.giveBack(l1);
System.out.println(b1 + " | " + ece.loanCount() + " " + lib.activeLoans());
System.out.println(Book.getCreated());
Ece -> Clean Code (on loan) for 14 days
Not available: Clean Code (on loan)
Limit reached: Ece
3 3
Clean Code | 2 2
4

Tasarım kararlarına dikkat: “kitap ödünçte mi” bilgisini Book tutuyor (available), “kaç kitabı var” bilgisini Member tutuyor, kuralları birleştiren Library. Her sınıf kendi verisinden sorumlu; bu, kapsüllemenin tasarım düzeyindeki karşılığı.

Sık yapılan hatalar

Yanlış düşünceDoğrusu
- x : int public alan- private; public +
UML’deki tip Java’da da sondaJava’da başta: int x, double getX()
Kalıtım üçgeni alt sınıfa bakarÜçgen üst sınıfa / arayüze bakar
Kesikli üçgen de extendsKesikli üçgen implements
Elmas parça tarafındaElmas bütün tarafında
Aggregation ve composition aynıComposition’da parça bütünsüz yaşamaz
0..* tek referans alanıKoleksiyon (List) alanı
Gereksinimdeki her isim bir sınıfBir şeyin özelliği olan isim alan olur; “system” sınıf değil
Altı çizili üye önemsiz süsstatic demek

Kendini test et

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

Soru 1kolay

In a UML class box, what does - balance : double mean?

Cevabı göster
private double balance alanı

Sınıf kutusunun ikinci bölmesi alanlardır. - private, ardından ad, iki noktadan sonra tip. Java'da: private double balance;. UML'de tip sonda yazılır, Java'da başta.

Soru 2kolay

What does an underlined operation + getCount() : int in a class box mean?

Cevabı göster
public static int getCount()

+ public. Altı çizili üye static demektir (sınıfa ait). Parantez içi parametreler, iki noktadan sonrası dönüş tipi. void dönen metotlarda : void yazılır ya da hiç yazılmaz.

Soru 3orta

Match each UML relationship line to Java: (1) solid line, hollow triangle head; (2) dashed line, hollow triangle head; (3) solid line, filled diamond at the whole.

Cevabı göster
(1) extends, (2) implements, (3) composition

İçi boş üçgen her zaman "is-a": düz çizgi sınıf kalıtımı (generalization, extends), kesikli çizgi arayüz gerçekleştirme (realization, implements). Üçgen üst sınıfa / arayüze bakar. Dolu elmas composition: elmas bütün tarafında durur, parçanın ömrü bütüne bağlıdır.

Soru 4orta

Which one is composition and which one is aggregation?

class Car {
    private final Engine engine;
    Car(int hp) { engine = new Engine(hp); }
}
class Team {
    private final List<Player> players = new ArrayList<>();
    void add(Player p) { players.add(p); }
}
Cevabı göster
Car-Engine composition, Team-Player aggregation

Car motorunu kendisi yaratıyor ve dışarıya vermiyor: motorun ömrü arabanınkine bağlı, dolu elmas (composition). Team oyuncuları dışarıdan alıyor; aynı oyuncu iki takımda olabilir ve takım silinse de oyuncu yaşar (denemede t1 = null sonrası t2 hâlâ 1 oyuncu tuttu): boş elmas (aggregation).

Soru 5orta

A UML association reads Member 1 ---- 0..3 Loan. What does it mean, and how is it usually coded in Member?

Cevabı göster
Her üyenin 0 ile 3 arası ödüncü olur; Member içinde List<Loan> alanı + sınır kontrolü

Çokluk (multiplicity) karşı uçta okunur: bir Member'a 0..3 Loan bağlı; her Loan'ın tam 1 Member'ı var. "Birden çok" taraf koleksiyon alanı olur (List<Loan> loans), üst sınır (3) kodda kontrol edilir: loans.size() < 3. Tek taraf (1) Loan içinde düz bir referans alanıdır: private Member member;.

Soru 6orta

Requirement: "A student enrolls in courses. Each course has a code and a capacity. The system rejects an enrollment when the course is full." Which words become classes, attributes and operations?

Cevabı göster
Sınıf: Student, Course. Alan: code, capacity. İşlem: enroll

İsim ayıklama (noun extraction): kendi verisi ve davranışı olan isimler sınıf (Student, Course), başka bir şeyin özelliği olan isimler alan (code, capacity). Fiiller işlem olur (enroll). "System" genelde sınıf yapılmaz; tüm programın kendisidir. "full" ayrı alan değil, enrolled == capacity ile hesaplanır.

Soru 7zor

What is printed?

// Library.lend(b, m, days): prints "Not available: <book>" if the book is on loan,
// "Limit reached: <name>" if the member already has 3 loans, otherwise creates a Loan.
// Book.toString(): title + (available ? "" : " (on loan)")
Loan l1 = lib.lend(b1, ece, 14);
System.out.println(l1);
lib.lend(b1, mert, 7);
lib.lend(b2, ece, 7);
lib.lend(b3, ece, 7);
lib.lend(b4, ece, 7);
System.out.println(ece.loanCount() + " " + lib.activeLoans());
lib.giveBack(l1);
System.out.println(b1 + " | " + ece.loanCount() + " " + lib.activeLoans());
Cevabı göster
Ece -> Clean Code (on loan) for 14 days / Not available: Clean Code (on loan) / Limit reached: Ece / 3 3 / Clean Code | 2 2

l1 basılırken kitap zaten ödünçte olduğu için toString "(on loan)" ekler. Mert aynı kitabı alamaz. Ece b2 ve b3'ü alır (3 ödünç), b4'te sınır dolu. Sayılar 3 3. İade sonrası kitap tekrar müsait (ek yok), Ece'de 2, kütüphanede 2 aktif ödünç.

Soru 8orta

Which UML element shows that Order only uses Printer temporarily, as a method parameter, without keeping a field?

  1. Association (solid line)
  2. Dependency (dashed arrow)
  3. Composition (filled diamond)
  4. Generalization (hollow triangle)
Cevabı göster
Dependency (dashed arrow)

Dependency (bağımlılık), "kullanır ama tutmaz" demektir: parametre, yerel değişken ya da static çağrı. Kesikli, ucu açık okla çizilir. Alan olarak tutulan referans association'dır (düz çizgi).

Soru 9orta

Write the Java class for this UML box.

+-------------------------------------------+
|                  Book                     |
+-------------------------------------------+
| - isbn : String  {readOnly}               |
| - title : String                          |
| - available : boolean = true              |
| - created : int          (underlined)     |
+-------------------------------------------+
| + Book(isbn : String, title : String)     |
| + getIsbn() : String                      |
| + isAvailable() : boolean                 |
| ~ setAvailable(a : boolean) : void        |
| + getCreated() : int     (underlined)     |
+-------------------------------------------+
Cevabı göster
Aşağıdaki sınıf (readOnly → final, altı çizili → static, ~ → belirleyicisiz)

Her satırı tek tek çevir. {readOnly} final alan, = true ilk değer, altı çizili üyeler static, ~ paket erişimi (belirleyici yazılmaz). Constructor her nesnede sayacı artırsın:

public class Book {
    private final String isbn;
    private String title;
    private boolean available = true;
    private static int created = 0;

    public Book(String isbn, String title) {
        this.isbn = isbn;
        this.title = title;
        created++;
    }
    public String getIsbn() { return isbn; }
    public boolean isAvailable() { return available; }
    void setAvailable(boolean a) { available = a; }
    public static int getCreated() { return created; }
}
Dört kitap yaratıldıktan sonra Book.getCreated() 4 döner.

Soru 10kolay

In a class diagram Staff has a solid line with a hollow triangle to abstract class *Person* (italic) and a dashed line with a hollow triangle to <<interface>> Payable. Write the class header, and say what s instanceof Payable prints for Person s = new Staff("Ayse", 4200);.

Cevabı göster
class Staff extends Person implements Payable; true

Düz çizgi + üçgen → extends, kesikli + üçgen → implements. İtalik ad abstract sınıf demek, Staff onun abstract metotlarını yazmak zorunda. Değişken Person tipinde olsa da nesne Staff, Staff da Payable: true. Denemede staff 4200.0 true basıldı.

Soru 11zor

Write class Course from the diagram Course (- code : String, - capacity : int) 1 ---- 0..* Student, with operations + enroll(s : Student) : boolean (false if the course is full or the student is already enrolled) and + getEnrolledCount() : int.

Cevabı göster
List<Student> alanı + kapasite ve tekrar kontrolü yapan enroll

0..* tarafı koleksiyon olur. İş kuralları (dolu mu, zaten kayıtlı mı) enroll içinde:

import java.util.ArrayList;
import java.util.List;

public class Course {
    private final String code;
    private final int capacity;
    private final List<Student> students = new ArrayList<>();

    public Course(String code, int capacity) {
        this.code = code;
        this.capacity = capacity;
    }
    public boolean enroll(Student s) {
        if (students.size() >= capacity || students.contains(s)) return false;
        students.add(s);
        return true;
    }
    public int getEnrolledCount() { return students.size(); }
    public String getCode() { return code; }
}
Kapasite 2 iken a, a, b, d sırasıyla enroll edilince true false true false, sayı 2.

Doğrulama: UML'den çevrilen bütün Java kodu JDK 26 ile çalıştırıldı (java verify/SEN1002/W07.java); çıktılar oradan alındı.

Deftere not kartı

Sınıf kutusu 3 bölme: ad / alanlar / işlemler. İtalik ad ya da {abstract} = abstract. + public - private # protected ~ package. Altı çizili = static. Alan: - ad : Tip [= ilk değer] İşlem: + ad(p : Tip) : DönüşTipi <<interface>> üstte yazılır. {readOnly} → final. Düz çizgi + boş üçgen → extends (generalization). Üçgen ÜST sınıfa bakar. Kesikli + boş üçgen → implements (realization). Düz çizgi → association (alan olarak tutar). Ok varsa yön: kim kimi biliyor. Kesikli ok → dependency (sadece kullanır: parametre / yerel). Boş elmas ◇ aggregation: parça bağımsız yaşar (Team-Player). Elmas BÜTÜN tarafında. Dolu elmas ◆ composition: parça bütünle doğar ölür (Car-Engine, House-Room). Çokluk: 1, 0..1, 0..*, 1..*, * . Karşı uçta okunur. "Çok" taraf → List alanı. Gereksinimden sınıf: isimler → sınıf/alan, fiiller → metot. "System" sınıf değil.