- Mevcut bir metodu temiz kod kurallarıyla yeniden düzenlemek
- SOLID'in beş ilkesini örneklerle açıklamak
- Singleton, Factory, Builder, Strategy ve Observer desenlerini tanımak ve yazmak
Bir program bir kez yazılır ama onlarca kez okunur: ekip arkadaşların ya da altı ay sonra sen kendin tarafından. Çalışan kod henüz iyi kod demek değildir: iyi kod kolay okunur, değiştirilir ve test edilir. Bu derste profesyonel Java geliştiricilerinin günlük kurallarını öğreneceksin: temiz kod ilkeleri, SOLID ve en çok kullanılan beş tasarım deseni.
Temiz kodun kuralları
- Anlamlı adlar:
c,r,calcdeğil;passedCount,average,averageOfPassed. Ad, neyin ne yaptığını söylemelidir. - Küçük metotlar: bir metot, bir iş. Bir metot ekrana sığmıyorsa onu parçalara böl.
- Sihirli sayılar yok:
50ve1.1yerinePASS_MARKveBONUS_FACTORsabitleri. - Değişmezlik: mümkün olan her yerde
finalalanlar verecord'lar; hiç değişmeyen bir nesne iş parçacıkları arasında da güvenlidir. - **
nullyerineOptional**: sonuç olmayabilirse metotOptional<Student>döndürsün ki çağıran bunu unutmasın. - İstisnaları yutma: boş bir
catch {}hatayı gizler; ya onu ele al ya da yukarı ilet.
public double calc(List<Integer> l, int t) {
double r = 0;
int c = 0;
for (int i = 0; i < l.size(); i++) {
if (l.get(i) != null) {
if (l.get(i) >= 50) { r = r + l.get(i); c++; }
}
}
if (t == 1) return c == 0 ? 0 : r / c * 1.1;
return c == 0 ? 0 : r / c;
}private static final int PASS_MARK = 50;
private static final double BONUS_FACTOR = 1.1;
public double averageOfPassed(List<Integer> scores, boolean withBonus) {
double average = scores.stream()
.filter(Objects::nonNull)
.filter(score -> score >= PASS_MARK)
.mapToInt(Integer::intValue)
.average()
.orElse(0);
return withBonus ? average * BONUS_FACTOR : average;
}İlk sürümde t == 1 ne demek? 1.1 nereden geldi? Okuyucu her satırı kafasında çalıştırmak zorunda kalıyor. İkinci sürümde ad, sabitler ve akış her şeyi anlatıyor; boolean withBonus ise gizemli int t'nin yerini aldı. Dikkat et: mantığı değiştirmedik; bu bir yeniden düzenlemedir (refactoring) ve önceden yazılmış testler (JUnit dersi!) davranışın korunduğunu garanti eder.
SOLID ilkeleri
SOLID, Robert C. Martin'in yaygınlaştırdığı beş nesne yönelimli tasarım ilkesinin baş harflerinden oluşur. Kodu değişikliklere dayanıklı yapar: yeni bir gereksinim geldiğinde on yeri değil, tek bir yeri değiştirirsin.
| Harf | İlke | Kısaca |
|---|---|---|
| S | Single Responsibility | Bir sınıfın değişmek için tek bir nedeni olmalıdır: denetleyici HTTP ile, servis kurallarla, depo veritabanıyla ilgilenir. |
| O | Open/Closed | Kod genişlemeye açık, değişikliğe kapalıdır: yeni bir kural, eski kod düzenlenmeden yeni bir sınıf olarak eklenir. |
| L | Liskov Substitution | Bir alt sınıf, üst sınıfının beklendiği her yerde beklentileri bozmadan çalışmalıdır. |
| I | Interface Segregation | Tek büyük arayüz yerine birkaç küçük arayüz: bir sınıf kullanmadığı metotları gerçekleştirmeye zorlanmamalıdır. |
| D | Dependency Inversion | Somut sınıflara değil, soyutlamalara (arayüzlere) bağlı ol; JUnit dersinde ReportService, ScoreRepository arayüzüne bağlıydı. |
Tasarım desenleri
Bir tasarım deseni (design pattern), sık karşılaşılan bir soruna yönelik denenmiş bir çözüm şemasıdır. 1994'te “Dörtlü Çete” (Gang of Four: Gamma, Helm, Johnson, Vlissides) 23 deseni bir kitapta topladı ve üç gruba ayırdı: yaratımsal (nesneler nasıl oluşturulur), yapısal (nesneler nasıl birleştirilir) ve davranışsal (nesneler nasıl iş birliği yapar). Aşağıda Java'da en sık göreceğin beş desen var.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
AppConfig.INSTANCE.set("school", "School No. 6, Baku");
System.out.println(AppConfig.INSTANCE.get("school"));
System.out.println(AppConfig.INSTANCE == AppConfig.valueOf("INSTANCE"));
for (String type : List.of("email", "sms")) {
Notifier notifier = Notifier.of(type);
notifier.send("Aysel", "Your exam score is 95");
}
}
}
enum AppConfig { // Singleton: exactly one instance
INSTANCE;
private final Map<String, String> values = new HashMap<>();
void set(String key, String value) { values.put(key, value); }
String get(String key) { return values.get(key); }
}
interface Notifier {
void send(String to, String text);
static Notifier of(String type) { // Factory: hides which class is created
return switch (type) {
case "email" -> (to, text) -> System.out.println("Email to " + to + ": " + text);
case "sms" -> (to, text) -> System.out.println("SMS to " + to + ": " + text);
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}
}School No. 6, Baku true Email to Aysel: Your exam score is 95 SMS to Aysel: Your exam score is 95
enum ile) ve Factory (statik fabrika metodu)Singleton (tekil nesne), bir sınıfın yalnızca bir nesnesi olmasını sağlar. Java'da en basit ve iş parçacığı güvenli yol tek elemanlı bir enum'dur; Spring bean'leri de varsayılan olarak singleton'dır. Factory (fabrika) nesneyi new yerine bir metotla oluşturur ve hangi sınıfın oluşturulduğunu gizler: çağıran yalnızca Notifier arayüzünü tanır. JDK'da List.of(...), Integer.valueOf(...) ve Path.of(...) fabrika metotlarıdır.
public class Main {
public static void main(String[] args) {
Report full = new Report.Builder("Aysel")
.subject("Math")
.score(95)
.comment("Excellent work")
.build();
Report minimal = new Report.Builder("Murad").build();
System.out.println(full);
System.out.println(minimal);
}
}
record Report(String student, String subject, int score, String comment) {
static class Builder { // Builder: readable step-by-step creation
private final String student;
private String subject = "General";
private int score;
private String comment = "";
Builder(String student) { this.student = student; }
Builder subject(String value) { subject = value; return this; }
Builder score(int value) { score = value; return this; }
Builder comment(String value) { comment = value; return this; }
Report build() { return new Report(student, subject, score, comment); }
}
}Report[student=Aysel, subject=Math, score=95, comment=Excellent work] Report[student=Murad, subject=General, score=0, comment=]
Bir yapıcının dört beş parametresi olduğunda new Report("Aysel", "Math", 95, "Excellent") çağrısında hangi değerin ne olduğunu anlamak zordur; isteğe bağlı parametreler için de çok sayıda yapıcı gerekir. Builder (inşacı) nesneyi adlandırılmış metotlarla adım adım kurar ve sonunda build() hazır, değişmez bir nesne döndürür. Tanıdık örnekler: StringBuilder, HttpClient.newBuilder(), Stream.builder().
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
GradingRule strict = raw -> raw; // Strategy 1
GradingRule withBonus = raw -> Math.min(100, raw + 5); // Strategy 2
GradeBoard board = new GradeBoard(withBonus);
board.subscribe((name, score) -> System.out.println("[parent app] " + name + " got " + score));
board.subscribe((name, score) -> System.out.println("[teacher log] " + name + ": " + score));
board.addScore("Leyla", 88);
board.setRule(strict);
board.addScore("Elvin", 88);
}
}
interface GradingRule { int apply(int raw); }
interface ScoreListener { void onScore(String name, int score); }
class GradeBoard {
private GradingRule rule;
private final List<ScoreListener> listeners = new ArrayList<>();
GradeBoard(GradingRule rule) { this.rule = rule; }
void setRule(GradingRule rule) { this.rule = rule; }
void subscribe(ScoreListener listener) { listeners.add(listener); }
void addScore(String name, int raw) {
int score = rule.apply(raw);
listeners.forEach(l -> l.onScore(name, score)); // Observer: notify everyone
}
}[parent app] Leyla got 93 [teacher log] Leyla: 93 [parent app] Elvin got 88 [teacher log] Elvin: 88
Strategy (strateji), bir algoritmayı dışarıdan verilebilen bir nesneye dönüştürür: GradeBoard, bonuslu ya da katı kuralın içinde ne olduğunu bilmez; yeni bir kural eklemek için onu düzenlemek gerekmez; bu, SOLID'in “O” harfidir. Comparator da bir stratejidir. Observer (gözlemci), bir nesnedeki değişikliği ona abone olan tüm nesnelere iletir: veli uygulaması ve öğretmen günlüğü aynı olayı alır, GradeBoard ise onlar hakkında hiçbir şey bilmez. Grafik arayüzlerdeki düğme dinleyicileri ve Spring olayları da böyle çalışır.
| Desen | Hangi sorunu çözer | JDK'da ve Spring'de |
|---|---|---|
| Singleton | tek örnek yeterli: yapılandırma, önbellek | Runtime.getRuntime() |
| Factory | hangi sınıfın oluşturulduğunu gizlemek | List.of(), Path.of() |
| Builder | çok parametreli bir nesneyi anlaşılır biçimde kurmak | StringBuilder, HttpClient.newBuilder() |
| Strategy | algoritmayı çalışma zamanında değiştirmek | Comparator |
| Observer | bir olayı birçok aboneye bildirmek | PropertyChangeListener, ApplicationEventPublisher |
Önemli noktalar
- Temiz kod: anlamlı adlar, küçük metotlar, sihirli sayılar yerine sabitler, değişmezlik,
Optional, boşcatchyok. - Yeniden düzenleme, davranışı değiştirmeden yapıyı iyileştirir; testler onu güvenli kılar.
- SOLID: tek sorumluluk, düzenlemeden genişletme, yerine geçebilen alt sınıflar, küçük arayüzler, soyutlamalara bağımlılık.
- Yaratımsal desenler: Singleton (
enum), Factory (of(...)), Builder (build()). - Davranışsal desenler: Strategy — değiştirilebilir algoritma (
Comparator), Observer — abonelere bildirim.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.