- Təmiz kodun qaydaları ilə mövcud metodu refaktorinq etmək
- SOLID-in beş prinsipini nümunələrlə izah etmək
- Singleton, Factory, Builder, Strategy və Observer nümunələrini tanımaq və yazmaq
Proqram bir dəfə yazılır, amma onlarla dəfə oxunur — komanda yoldaşları, ya da altı aydan sonra sən özün tərəfindən. İşləyən kod hələ yaxşı kod demək deyil: yaxşı kodu asan oxumaq, dəyişmək və test etmək olur. Bu dərsdə peşəkar Java proqramçılarının gündəlik qaydalarını — təmiz kod prinsiplərini, SOLID-i və ən çox işlənən beş dizayn nümunəsini öyrənəcəksən.
Təmiz kodun qaydaları
- Mənalı adlar:
c,r,calcyox,passedCount,average,averageOfPassed. Ad nə etdiyini deməlidir. - Kiçik metodlar: bir metod — bir iş. Metod ekrana sığmırsa, onu hissələrə böl.
- Sehrli ədədlər yox:
50və1.1əvəzinəPASS_MARKvəBONUS_FACTORsabitləri. - Dəyişməzlik: mümkün olan yerdə
finalsahələr vərecord-lar; dəyişməyən obyekt thread-lər arasında da təhlükəsizdir. - **
nulləvəzinəOptional**: nəticə olmaya bilərsə, metodOptional<Student>qaytarsın ki, çağıran bunu unutmasın. - İstisnaları «udma»: boş
catch {}səhvi gizlədir; ya onu emal et, ya da yuxarı ötür.
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;
}Birinci versiyada t == 1 nə deməkdir? 1.1 haradan gəlib? Oxucu hər sətri beynində icra etməli olur. İkinci versiyada ad, sabitlər və axın hər şeyi deyir, boolean withBonus isə sirli int t-ni əvəz edib. Diqqət et: məntiqi dəyişməmişik — bu, refaktorinqdir, əvvəlcədən yazılmış testlər isə (JUnit dərsi!) davranışın qorunduğuna zəmanət verir.
SOLID prinsipləri
SOLID — Robert Martinin məşhurlaşdırdığı beş obyektyönlü dizayn prinsipinin baş hərfləridir. Onlar kodu dəyişikliklərə davamlı edir: yeni tələb gələndə on yeri deyil, bir yeri dəyişirsən.
| Hərf | Prinsip | Qısaca |
|---|---|---|
| S | Single Responsibility | Sinfin dəyişmək üçün yalnız bir səbəbi olmalıdır: kontroller HTTP ilə, servis qaydalarla, repozitori baza ilə məşğuldur. |
| O | Open/Closed | Kod genişlənmə üçün açıq, dəyişiklik üçün qapalıdır: yeni qayda köhnə kodu redaktə etmədən yeni sinif kimi əlavə olunur. |
| L | Liskov Substitution | Törəmə sinif valideyn sinfin gözlənildiyi hər yerdə işləməli və gözləntiləri pozmamalıdır. |
| I | Interface Segregation | Bir böyük interfeys əvəzinə bir neçə kiçik interfeys: sinif istifadə etmədiyi metodları reallaşdırmağa məcbur olmamalıdır. |
| D | Dependency Inversion | Konkret sinifdən yox, abstraksiyadan (interfeysdən) asılı ol — JUnit dərsində ReportService ScoreRepository interfeysindən asılı idi. |
Dizayn nümunələri
Dizayn nümunəsi (design pattern) tez-tez rastlaşan problemin sınaqdan çıxmış həll sxemidir. 1994-cü ildə «Dördlər dəstəsi» (Gang of Four: Gamma, Helm, Johnson, Vlissides) 23 nümunəni bir kitabda topladı və onları üç qrupa böldü: yaradıcı (obyektlər necə yaradılır), struktur (obyektlər necə birləşdirilir) və davranış (obyektlər necə əməkdaşlıq edir). Aşağıda Java-da ən çox görəcəyin beş nümunə 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 ilə) və Factory (statik fabrik metodu)Singleton sinifdən yalnız bir obyekt olmasını təmin edir. Java-da ən sadə və thread-lər üçün təhlükəsiz yol tək elementli enum-dur; Spring bean-ləri də susmaya görə singleton-dur. Factory isə obyekti new ilə yox, metodla yaradır və hansı sinfin yarandığını gizlədir: çağıran yalnız Notifier interfeysini tanıyır. JDK-da List.of(...), Integer.valueOf(...) və Path.of(...) fabrik metodları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=]
Konstruktorun dörd-beş parametri olanda new Report("Aysel", "Math", 95, "Excellent") çağırışında hansı qiymətin nə olduğunu anlamaq çətindir, məcburi olmayan parametrlər üçün isə çoxlu konstruktor lazım gəlir. Builder obyekti addım-addım, adlı metodlarla qurur və sonda build() hazır, dəyişməz obyekt qaytarır. Tanış nümunələr: 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 alqoritmi kənardan ötürülə bilən obyektə çevirir: GradeBoard bonuslu və ya sərt qaydanın içində nə olduğunu bilmir, yeni qayda əlavə etmək üçün onu redaktə etmək lazım deyil — bu, SOLID-in «O» prinsipidir. Comparator də strategiyadır. Observer bir obyektin dəyişikliyini ona abunə olmuş bütün obyektlərə çatdırır: valideyn tətbiqi və müəllim jurnalı eyni hadisəni alır, GradeBoard isə onlar haqqında heç nə bilmir. Qrafik interfeysdəki düymə dinləyiciləri və Spring-in hadisələri də belə işləyir.
| Nümunə | Hansı problemi həll edir | JDK-da və Spring-də |
|---|---|---|
| Singleton | bir nüsxə kifayətdir: konfiqurasiya, keş | Runtime.getRuntime() |
| Factory | hansı sinfin yaradıldığını gizlətmək | List.of(), Path.of() |
| Builder | çoxparametrli obyekti aydın qurmaq | StringBuilder, HttpClient.newBuilder() |
| Strategy | alqoritmi işləmə vaxtı dəyişmək | Comparator |
| Observer | hadisə barədə çoxlu abunəçiyə xəbər vermək | PropertyChangeListener, ApplicationEventPublisher |
Əsas fikirlər
- Təmiz kod: mənalı adlar, kiçik metodlar, sehrli ədədlər əvəzinə sabitlər, dəyişməzlik,
Optional, boşcatchyox. - Refaktorinq davranışı dəyişmədən quruluşu yaxşılaşdırır; testlər onu təhlükəsiz edir.
- SOLID: bir məsuliyyət, redaktəsiz genişlənmə, törəmə siniflərin əvəzlənməsi, kiçik interfeyslər, abstraksiyadan asılılıq.
- Yaradıcı nümunələr: Singleton (
enum), Factory (of(...)), Builder (build()). - Davranış nümunələri: Strategy — dəyişdirilə bilən alqoritm (
Comparator), Observer — abunəçilərə bildiriş.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.