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ş.
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:
- Görünürlük işaretleri:
+public,-private,#protected,~package. - UML’de tip sonda yazılır:
- name : String→private String name;. - Boş üçgen “is-a”: düz çizgi
extends, kesikli çizgiimplements. - Elmas “has-a”: boş elmas aggregation, dolu elmas composition. Elmas bütün tarafında.
- Ç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
+----------------------------------------------+
| UML | Java |
|---|---|
- balance : double = 0.0 | private double balance = 0.0; |
+ deposit(amount : double) : void | public void deposit(double amount) |
# audit() : boolean | protected boolean audit() |
~ setAvailable(a : boolean) | void setAvailable(boolean a) (belirleyici yok) |
| Altı çizili üye | static |
İ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’dadouble 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 | Çizim | Anlamı | Java karşılığı |
|---|---|---|---|
| Generalization (kalıtım) | Düz çizgi, boş üçgen üst sınıfta | is-a | class Dog extends Animal |
| Realization | Kesikli çizgi, boş üçgen arayüzde | can-do | class Invoice implements Payable |
| Association | Düz çizgi (ok varsa yön) | bilir / tutar | Alan: private Member member; |
| Aggregation | Düz çizgi, boş elmas bütünde | has-a, parça bağımsız | Dışarıdan gelen nesne listede tutulur |
| Composition | Düz çizgi, dolu elmas bütünde | has-a, parça bütüne ait | Parçayı bütün kendisi yaratır |
| Dependency | Kesikli ok | geçici kullanır | Parametre 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
newile 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ım | Anlamı | Kodda |
|---|---|---|
1 | tam bir | Referans alanı, null olmamalı |
0..1 | yok ya da bir | Referans alanı, null olabilir |
0..* ya da * | sıfır ya da çok | List<T> alanı |
1..* | en az bir | List<T>, boş bırakılmaz |
0..3 | en 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üşünce | Doğrusu |
|---|---|
- x : int public alan | - private; public + |
| UML’deki tip Java’da da sonda | Java’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 extends | Kesikli üçgen implements |
| Elmas parça tarafında | Elmas 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ıf | Bir şeyin özelliği olan isim alan olur; “system” sınıf değil |
| Altı çizili üye önemsiz süs | static demek |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
In a UML class box, what does - balance : double mean?
Cevabı göster
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.
What does an underlined operation + getCount() : int in a class box mean?
Cevabı göster
+ 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.
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
İç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.
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 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).
A UML association reads Member 1 ---- 0..3 Loan. What does it mean, and how is it usually coded in Member?
Cevabı göster
Ç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;.
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
İ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.
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
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ç.
Which UML element shows that Order only uses Printer temporarily, as a method parameter, without keeping a field?
- Association (solid line)
- Dependency (dashed arrow)
- Composition (filled diamond)
- Generalization (hollow triangle)
Cevabı göster
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).
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
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.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
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ı.
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
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ı.