Low-Level Design nədir?
Low-Level Design (LLD) bir funksiyanın və ya modulun daxilində kodun necə qurulacağını müəyyənləşdirməkdir. Hansı obyektlər var, hər biri nəyə cavabdehdir, hansı məlumatı saxlayır və bir-biri ilə necə işləyir? Məqsəd tələbi aydın məsuliyyətlərə, metodlara və yoxlanıla bilən davranışlara çevirməkdir.
“Sistem nə etməlidir?” sualını cavablandırmaq başlanğıcdır. LLD növbəti addımı açır: bu davranışı hansı hissə icra edəcək, vəziyyət harada saxlanacaq, hansı dəyişikliklərə icazə veriləcək və səhv sorğu gələndə nə baş verəcək?
Low-level sözü burada assembler və ya prosessor səviyyəsi demək deyil. Müqayisə sistemin ümumi arxitekturası ilədir: xidmətlərin böyük mənzərəsindən bir modulun siniflərinə, interfeyslərinə və davranış qaydalarına yaxınlaşırıq.
LLD nə üçün lazımdır?
Bir funksiya ilk dəfə işləyə bilər, amma qaydalar müxtəlif yerlərə səpələnibsə onu dəyişmək çətinləşir. Eyni yoxlama üç yerdə təkrarlanır, bir sahəni dəyişən kod başqa qaydanı unudur, xəta baş verəndə obyekt yarımçıq vəziyyətdə qalır. LLD bu riskləri məsuliyyət və sahiblik sərhədləri ilə azaldır.
Komandada bu sərhədlər ortaq anlaşma yaradır. Metodun adı, qəbul etdiyi giriş və verdiyi nəticə digər proqramçıya ondan necə istifadə edəcəyini göstərir. Daxili məlumatı hər kəs dəyişə bilmədikdə qaydanı qorumaq və ayrıca test etmək asanlaşır.
- Düzgünlük: icazəsiz vəziyyət keçidlərinin qarşısını almaq.
- Oxunaqlılıq: konkret qaydanın kodda harada olduğunu tapmaq.
- Test edilə bilmə: davranışı UI və verilənlər bazasından ayrı yoxlamaq.
- Dəyişiklik: yeni tələbin hansı hissəyə təsir etdiyini məhdudlaşdırmaq.
LLD, OOD və System Design arasındakı fərq
System Design sistemin əsas hissələrini və onların birlikdə işləməsini araşdırır: xidmət sərhədləri, məlumatın saxlanması, trafik, etibarlılıq və paylanmış sistem kompromisləri. LLD isə seçilmiş hissənin daxilində məlumatı, davranışı və vəziyyət keçidlərini modelləşdirir.
Bunlar bir-birindən tam ayrı dünyalar deyil. Arxitektura qərarı modulun müqaviləsinə təsir edir; modulun məhdudiyyəti də böyük sistemdə nəzərə alınmalıdır. Sadəcə müzakirənin miqyası və detalları fərqlənir.
Object-Oriented Design (OOD) obyekt yönümlü dizayndır. Müsahibələrdə OOD və LLD adları tez-tez oxşar format üçün işlədilir. LLD daha geniş anlayışdır; bu kursda onu əsasən Java sinifləri, interfeysləri və obyektlər arasındakı əməkdaşlıqla öyrənirik.
| Sual | System Design baxışı | LLD baxışı |
|---|---|---|
| Nəyi ayırırıq? | Xidmətləri, saxlama və mesajlaşma hissələrini | Sinifləri, interfeysləri və məsuliyyətləri |
| Nəyi modelləşdiririk? | Xidmətlərarası məlumat axınını və nasazlıq sərhədlərini | Metod çağırışlarını və obyektin vəziyyət keçidlərini |
| Nəyi əsaslandırırıq? | Miqyas, etibarlılıq və ardıcıllıq seçimlərini | Düzgünlük, sahiblik, asılılıqlar və dəyişdirilə bilməni |
| Nəticə necə görünür? | Arxitektura sxemi və qərarların izahı | Sinif müqavilələri, psevdokod və ya işlək kod |
Eyni məhsula iki fərqli yaxınlaşma
Taksi sifarişi məhsulunu düşün. System Design müzakirəsində sürücülərin yerləşməsi, uyğunlaşdırma xidməti, səfər məlumatları və bildirişlər arasında axını göstərərdik. Pik trafik, məlumatın yenilənməsi və xidmət dayananda davranış əsas suallar olardı.
LLD müzakirəsində isə bir səfərin daxilinə baxarıq: Trip hansı vəziyyətləri saxlayır? Sürücü təyin edilmədən səfər başlaya bilərmi? Tamamlanmış səfəri ləğv etmək olarmı? Qiymət hesablanması dəyişəndə səfərin keçid qaydalarını da dəyişməliyikmi?
Bu suallar sinif sayını əvvəlcədən müəyyən etmir. Əvvəl davranışı dəqiqləşdiririk, sonra həmin davranışın sahibini seçirik.
| Məsuliyyət | Mümkün model | Cavablandırdığı sual |
|---|---|---|
| Səfərin həyat dövrü | Trip və TripState | İndiki vəziyyətdən hansı keçidə icazə var? |
| Qiymət qaydası | PricingPolicy | Qiymətin hesablanması səfər qaydalarından ayrı dəyişirmi? |
| Məkan məlumatı | Location | Koordinatlar hansı yoxlanılmış dəyəri ifadə edir? |
Bu kursu necə öyrənəcəyik?
Əvvəl dizaynı necə təqdim etməyi, məsuliyyətləri necə ayırmağı və obyekt yönümlü anlayışları öyrənəcəyik. Design pattern-ləri isə konkret dəyişiklik ehtiyacına cavab verdikləri yerdə istifadə edəcəyik. Pattern adı qərarın əsaslandırılmasını əvəz etmir.
Sonra paralel işə keçəcəyik: eyni məlumatın düzgün dəyişməsi (correctness), işçilərin bir-birini gözləməsi və məlumat ötürməsi (coordination), məhdud resursların bölüşdürülməsi (scarcity). Bu anlayışları öyrəndikdən sonra praktik dizaynlarda tətbiq edəcəyik.
Kursda 9 praktik dizayn var: Connect Four, Amazon Locker, lift, dayanacaq, fayl sistemi, kino bileti rezervasiyası, logging xidməti, rate limiter və inventar idarəetməsi. Hər nümunədə problemi anlamaqdan başlayıb tələblər, məsuliyyətlər, sinif dizaynı və tam Java koduna keçirik.
- Java-da sinif, metod, kolleksiya və exception əsaslarını bilməyin kifayətdir; hər pattern-i əvvəlcədən əzbərləmək lazım deyil.
- Vizuallarda növbəti addımdan əvvəl nəticəni özün təxmin et.
- Tam kodu açmazdan əvvəl əsas sinifləri və 2-3 metod müqaviləsini yaz.
- Həlli oxuduqdan sonra bir yeni tələb əlavə et və hansı hissənin dəyişdiyini izah et.
Müsahibədə səndən nə gözlənilir?
LLD müsahibəsində adətən məhdud bir problem verilir və onun kod strukturunu qurmağın istənilir. Gözlənti şirkətə, komandaya və səviyyəyə görə dəyişir. Bəzi formatlarda psevdokod, digərlərində işlək və test edilən proqram tələb olunur.
Vaxtı, istifadə ediləcək dili, kodun işlədilib-işlədilməyəcəyini və paralel sorğuların əhatəyə daxil olub-olmadığını dəqiqləşdir. Coğrafiyaya və şirkətin ölçüsünə əsasən formatı təxmin etməkdənsə, konkret gözləntini soruş.
Sən: Sinifləri və əsas metodları göstərməyim kifayətdir, yoxsa kodu kompilyasiya edib test də etməliyəm?
Müsahibəçi: Bu formatda əsas axının işləməsini və sərhəd testlərini görmək istəyirik.
Sən: Model bir proses daxilində işləyəcək? Verilənlər bazası və paralel müraciətlər də tələb olunur?
Müsahibəçi: Yaddaşdakı model kifayətdir. Paralel müraciəti sonrakı tələb kimi müzakirə edəcəyik.
Dizayn hansı meyarlarla qiymətləndirilir?
Yaxşı cavabda qərarlar bir-birini izah edir: tələb məsuliyyətə, məsuliyyət metoda, metod isə testə çevrilir. Hər sinfin niyə mövcud olduğunu və hansı qaydanı qoruduğunu deyə bilməlisən.
Zəif başlanğıc: tələbsiz struktur
Problemi eşidən kimi on sinif və bir neçə pattern yazmaq əhatəni aydınlaşdırmır. Sonradan tələbi həmin struktura uyğunlaşdırmağa çalışmalı olursan.
Daha sağlam başlanğıc: davranışdan məsuliyyətə
Əvvəl uğurlu və uğursuz bir axını izah et. Qorunmalı qaydanı yaz, onu hansı obyektin qoruyacağını seç, sonra metod imzalarını qur.
| Meyar | Güclü cavabda nə görünür? |
|---|---|
| Problemi anlama | Əsas axın, əhatə və uğursuz hallar koddan əvvəl dəqiqləşir. |
| Sinif dizaynı | Vəziyyətin sahibi və hər hissənin məsuliyyəti aydındır. |
| Kod keyfiyyəti | Adlar niyyəti göstərir, giriş yoxlanır, daxili vəziyyət qorunur. |
| Dəyişdirilə bilmə | Yeni tələb üçün uyğun sərhəd var, lazımsız ümumiləşdirmə yoxdur. |
| Ünsiyyət | Seçimin səbəbi, alternativi və məhdudiyyəti açıq izah edilir. |
| Yoxlama | Yalnız uğurlu yol deyil, qadağan keçid və xəta sonrası vəziyyət də test olunur. |
İndi kiçik nümunə: kitab nüsxəsinin verilməsi
Anlayışları konkretləşdirmək üçün bir fiziki kitab nüsxəsini modelləşdirək. Kitabın adı və müəllifi kataloq məlumatıdır. Bizim sualımız isə budur: bu nüsxə hazırda kimdədir və onu başqa oxucu götürə bilərmi?
Əhatə kiçikdir: nüsxə bir oxucuya verilir və həmin oxucu tərəfindən qaytarılır. Axtarış, cərimə, istifadəçi autentifikasiyası və rezervasiya növbəsi daxil deyil. Oxucu identifikatoru etibarlı giriş sərhədindən gələn, boş olmayan sətirdir.
Qorunan qayda: bir nüsxənin eyni anda ən çox bir borcalanı var. Artıq verilmiş kitabı başqa oxucu götürməyə çalışanda sahib dəyişməməlidir. Aşağıdakı ssenaridə hər addım bir əməliyyatı göstərir.
Boş nüsxə
Hələ heç kim kitabı götürməyib.
Əvvəl davranış, sonra struktur
Kitabın adına və müəllifinə baxmaqla borc vermə qaydasını tapmaq olmur. “Eyni nüsxə eyni anda yalnız bir oxucuda ola bilər” cümləsi isə birbaşa modelə yol göstərir: BookCopy cari borrowerId-ni saxlayır və borrow əməliyyatında boş olub-olmadığını yoxlayır. Bu, invariantdır - icazəli hər əməliyyatdan sonra doğru qalmalı qayda.
Müştəri koduna setBorrower adlı sərbəst setter vermirik. borrow və returnBy metodları niyyəti ifadə edir və keçidin şərtlərini qoruyur. Adları yaxşı seçilmiş kiçik API həm yanlış istifadənin qarşısını alır, həm də test yazmağı asanlaşdırır.
Nümunənin müqaviləsi və sinif sərhədi
BookCopy cari borcalanı gizli sahədə saxlayır. Çağıran onu birbaşa dəyişə bilmir; borrow(reader) və returnBy(reader) ilə niyyətini bildirir. Beləliklə, yoxlama və vəziyyət dəyişikliyi eyni məsuliyyətin içində qalır.
Bu ilk nümunə üçün bir sinif kifayətdir. Bütün nüsxələr üzrə axtarış əlavə olunsa, ayrıca xidmət nüsxəni tapıb sorğunu ona yönləndirə bilər. Oxucunun ümumi borc limiti tələb olunanda isə həmin qaydanın sahibi bir nüsxədən daha geniş sərhəddə olmalıdır.
| Əməliyyat | Şərt | Nəticə |
|---|---|---|
| borrow(reader) | Reader boş deyil və nüsxə sərbəstdir | true; cari borcalan reader olur |
| borrow(reader) | Nüsxə artıq verilib | false; əvvəlki borcalan saxlanır |
| returnBy(reader) | Reader cari borcalandır | true; nüsxə sərbəst olur |
| returnBy(reader) | Nüsxə boşdur və ya reader başqa şəxsdir | false; vəziyyət dəyişmir |
| Hər iki metod | Reader null və ya boşdur | IllegalArgumentException; vəziyyət dəyişmir |
Kodda qaydanı necə qoruyuruq?
borrow əvvəl girişi yoxlayır, sonra nüsxə sərbəstdirsə borcalanı yazır. returnBy yalnız cari borcalan uyğun gələndə sahəni boşaldır. null burada “aktiv borcalan yoxdur” mənasını daşıyır; ayrıca borrowed bayrağı saxlamırıq.
Metodlardakı synchronized eyni Java obyektinə gələn çağırışların yoxlama və dəyişmə hissələrini bir-birinə qarışmadan icra edir. Hələ kilidlərin bütün detallarını bilməyin lazım deyil; əsas fikir vəziyyətin yoxlanması ilə dəyişdirilməsinin ayrılmamasıdır. Paralel iş modulunda bunu ayrıca öyrənəcəyik.
Bu qoruma yalnız həmin obyektin yerləşdiyi prosesə aiddir. Başqa serverdəki ayrıca obyektlə koordinasiya yaratmır. Davamlı saxlama və çox serverli işləmə əlavə olunanda uyğun tranzaksiya və məlumat ardıcıllığı müqaviləsi ayrıca qurulmalıdır.
Dizaynı davranışla yoxla
A nüsxəni götürsün, B-nin götürməsi rədd edilsin. Ardınca B qaytarmağa çalışsın: nəticə yenə false olmalı və A-nın borcu itməməlidir. Yalnız A qaytarandan sonra B götürə bilər. Bu ardıcıllıq həm nəticəni, həm də qorunan vəziyyəti yoxlayır.
Null və boş identifikatorları ayrıca yoxla. Exception-dan sonra nüsxənin əvvəlki sahibinin dəyişmədiyinə əmin ol. Təkcə “xəta atıldı” yoxlaması yarımçıq dəyişmiş vəziyyəti aşkar etməyə bilər.
Gələcəyin bütün tələblərini indidən qurmaq
Cərimə tələb olunmayıbsa PaymentGateway, hadisə şini və geniş irsiyyət ağacı əlavə etmək əsas qaydanı görünməz edir.
Yeni tələb gələndə sərhədi yenidən qiymətləndirmək
Tarixçə lazım olsa ayrıca borc qeydləri əlavə et. “Oxucu ən çox 3 kitab götürə bilər” tələbi gəlsə, bütün nüsxələri əhatə edən qayda sahibini seç. Cari nüsxənin qaydasını itirmədən modeli genişləndir.
Növbəti dərsə hazırlaş
LLD-ni indi bir cümlə ilə izah etməyə çalış: məhdud bir problemin davranışını aydın məsuliyyətlərə, metodlara və qorunan vəziyyətə çeviririk. Yaxşı dizaynı qutu və pattern sayı ilə deyil, qaydanın düzgünlüyü və dəyişikliklərin idarə oluna bilməsi ilə qiymətləndiririk.
Növbəti dərsdə bu işi müsahibə vaxtına sığan ardıcıllıqla aparacağıq: problemi dəqiqləşdirmək, əsas axını seçmək, modeli qurmaq, kritik davranışı yazmaq və dizaynı yoxlamaq. İndi aşağıdakı kodu işə sal, sonra oxucu limiti tapşırığını özün düşün.
- LLD ilə System Design-ın fərqini eyni məhsul üzərindən izah edə bilirəm.
- Bir invariant yazıb onu hansı obyektin qoruduğunu göstərə bilirəm.
- Metodun uğurlu, rədd və etibarsız giriş nəticələrini ayıra bilirəm.
- Yeni tələb gələndə hansı sərhədin dəyişəcəyini əsaslandıra bilirəm.