Skip to content

SOLID Prinsiplər Java ilə

Published:
6 min read

S.O.L.I.D prinsiplər Java ilə

Obyekt yönümlü proqramlaşdırma (OOP) dünyasında bir çox dizayn qaydaları, nümunələri və ya prinsipləri mövcuddur. Bunlardan ən popular və çox istifadə ediləni isə SOLID-dir. S.O.L.I.D obyekt yönümlü proqramlaşdırmada 5 əsas dizayn prinsiplərini bildirən bir qısaltmadır. Bu prinsiplər proqram təminatçılarına asanlıqla genişlənə və idarə edilə bilən proqram təminatını inkişaf etdirməyə imkan verir. Həmçinin SOLID prinsiplər, proqram təminatının asanlıqla yeni dəyişim tələblərinə adaptasiya ola bilməsini təmin edir. OOP-nin 5 dirəyi

SOLID prinsipləri ilk olaraq Robert C. Martin (Uncle Bob) tərəfindən 2000-ci ildə hazırlanmış Dizayn Prinsipləri və Dizayn Nümunələri məqaləsində konseptuallaşdırılmışdır. Bu anlayışlar sonradan bizi SOLID qısaltması ilə tanış edən Michael Feathers tərəfindən qurulmuşdur. Və son 20 ildə bu 5 prinsip, proqram yazma tərzimizi dəyişdirərək, obyekt yönümlü proqramlaşdırma dünyasında inqilab etdi.

SOLID nədir və daha yaxşı kod yazmağımıza necə kömək edir? Qısaca desək, Martin və Feathers-in dizayn prinsipləri bizi daha davamlı, başa düşülən və çevik bir proqram yaratmağa təşviq edir. Nəticə etibarilə tətbiqlərimiz böyüdükcə onların mürəkkəbliyini azalda bilərik və beləliklə baş ağrılarından da qurtula bilərik. Aşağıdakı 5 konsepsiya SOLID prinsipləri təşkil edir:

[S]ingle Responsibility Principle [O]pen/Closed Principle [L]iskov Substitution Principle [I]nterface Segregation Principle [D]ependency Inversion Principle

Single Responsibility Principle

Bu prinsipi qısaca izah emtək istəsək Uncle Bob-dan sitat gətirə bilərik. Bob bu prinsipi belə izah edir:

Bir sinifin dəyişməsi üçün yalnız və yalnız bir səbəb olmalıdır.

Başqa sözlə desək, hər sinif yalnız bir məqsəd üçün cavabdeh olmalıdır. Bunun üçün proqram təminatını nümunə çəkə bilərik. Hərhansı bir proyektdə frontend yalnız özünə aid hissə ilə məşğul olacağ, yəni tətbiqin frontend-i ilə yalnız. Bu tətbiqin digər bölmələri üçündə belədir. Testerlər yalnız test mərhələsinə fokuslanır, backend isə yalnız backend, və.s. Məsələn, aşağıdaki sadə bir kitabı təmsil edən sinifi nəzərdən keçirək:

Bu kodda bir Kitab nümunəsi ilə əlaqəli ad, müəllif və mətn saxlayırıq. İndi mətn üzərində əməliyyat aparmaq üçün birneçə metod əlavə edək:

Yuxarıdaki koddan da görnüdüyü kimi artıq rahatlıqla, istədiyimiz qədər kitab yaradıb və saxlaya bilərik, həmçinin onları çap edib və başqa yerlərərdə yaza bilərik. Və bunların hamısı yalnız bir sinif daxilindədir. Lakin bu kod yuxarıda qeyd etdiyim Single Responsibility Principle pozur. Bu problemi həll etmək üçün, sinifi daha kiçik hissələrə bölərək əmin olurqki, hərbir sinifin yalız bir məsuliyyəti var:

Artığ Kitab məlumatlarını saxlayan və Kitab məlumatların çap edən fərqli və daha sadə iki sinif var.

Open for Extension, Closed for Modification

Bu prinsip “proqram subyektləri (siniflər, modullar, funksiyalar və s.) Genişləndirmə üçün açıq, lakin modifikasiya üçün bağlı olmalıdır” deyir. Başqa bir sözlə, sinif davranışını dəyişdirmədən onu genişlətməyi (böyütməyi) bacarmalısınız. . Ətraflı olaraq desək, inheritance, interface və composition yolu ilə bir sinif davranışını genişləndirə bilərik, ancaq bunula sinifə kiçik müdaxilələrlə dəyişkilik edə bilmərik. Daha ətraflı bu prisipə baxmaq üçün aşağıdaki kod nümunəsinə nəzər yetirə bilərik. Sadə bir Telefon sinifnə sahib olduğumuzu təssəvür edək:

Kodu işlətsək hərşey əladır, Telefonun adı, modeli və yaddaşına dair məlumatlara sahibik. Lakin bir müddətdən sonra bu məlumatlar yetərsiz gələ bilər, və daha ağıılı bir telefon yaratrmağa ehtiyac duya bilərik. Bizə lazım ola biləcək yeni Telefon funksiyalarlını birbaşa sinifə əlavə edə bilərik. Yada Open/Close prinisbinə sadiq qalıb bu əlavə funksiyaları ayrı siniflər arasında paylaya bilərik:

Yeni yaratdığımız AğıllıTelefon sinifni Telefon-dan extend edərək, Sahib olduğumuz koda heçbir müdaxilə etmədən onun funksionallığını artırırğ. Robert C. Martin Design Principles Figure 2–13

Liskov Substitution Principle

Liskov Substitution prinsipi bəlkədə ən qəliz SOLID prinsisibidir. Bu prinsipi Barbara Liskov məlumat toplama və tip nəzəriyyəsinə dair işində əsaslandırmışdır.

Alt siniflər baza sinifləri ilə əvəzoluna bilməlidir.

Sadə izah etməyə çalışsaq, A sinif B sinifinin alt sinifidirsə, onda proqramımızın davranışını pozmadan B-ni A ilə əvəz edə bilməliyik.

Kod ilə bunu izah etmək üçün sadə bir sinif yaradaq:

Yuxarıdaki interface sadə Canlı-nı təmsil edir. Bütün canlılar nəfəs alır və yemək yeyir. İnterface-i istaifadə etmək üçün birneçə sinif yaradaq:

Canlı sinif implement edən iki (Heyvan və Bitki) alt sinif yaratdıq. Bu siniflər Canlı sinifnə aid bütün metodları özlərində birləşdirməklə yanaşı, özlərinə məxsus metdolara da sahibdirlər.

Liskov Substitution prinsibini icra etməklə biz, bu hər iki alt sinifi baza sinif əvəzində işlədə bilməliyik:

Beləliklə Canli baza sinifinin alt sinifləri onu istənilən mövqedə asanlıqla əvəz edə bilməlidir, və bizim nümunədə də əvəz edir.

Interface Segregation Principle

Prinsip sadəcə daha böyük interfeyslərin daha kiçiklərə bölünməsini tələb edir. Bunu etməklə, tətbiq olunan siniflərin yalnız onları maraqlandıran metodlardla əlaqədə olamalarını təmin edə bilərik. Aşağıdaki koda nəzər yetirək:

“Quş” interface-də bizə lazım ola biləcək bütün metodları biryerdə yığmışıq. Lakin buda öz növbəsində bizə gələcəkdə çətinlik yatamağla yanaşı Interface Segregation prinsipin pozur. Beləki əgər Quş interface-ni implement edən yeni siniflər yaratsağ belə bir problemlə qarşılaşa bilərik:

Yuxarıdaki siniflərdən də göründüyü kimi Ördək və Pinqvin-də Qush-dur və Qush interface-ni implement edərək, Qush-un sahib olduğu metodları Override edir. Lakin burada belə bir problem ortaya çıxır, Pinqvin bir Qush olmağına baxmayaraq, uça bilmir və ucmaq deyə bir metoda sahib ola bilməz. Bir interface-mizi xırda interface-lərə parçalayaraq istədiyimiz nəticəni əldə edə bilərik:

Bundan sonra sadəcə bizə lazım olan interface-ləri implement etməklə sinifimzə, daha səliqəli və qəbul edilə bilən kod əldə edə bilirik:

Dependency Inversion Principle

Bu prinsipin ümumi fikri vacib olduğu qədər sadədir: Mürəkkəb məntiqi təmin edən yüksək səviyyəli modullar asanlıqla təkrar istifadə oluna və aşağı səviyyəli modullarda dəyişikliklərə məruz qalmamalıdır. Buna nail olmaq üçün yüksək səviyyəli və aşağı səviyyəli modulları bir-birindən ayıran bir abstraksiya tətbiq edilməlidir. Bu ideyaya əsaslanaraq Robert C. Martin’in Dependency Inversion prinsipinin tərifi iki hissədən ibarətdir:

1. Yüksək səviyyəli modullar aşağı səviyyəli modullardan asılı olmamalıdır. Hər ikisi də abstraksiyalardan asılı olmalıdır.
2. Abstraksiyalar detallardan asılı olmamalıdır. Detallar abstraksiyalardan asılı olmalıdır.

Bu tərifin vacib bir detalı, yüksək səviyyəli və aşağı səviyyəli modulların abstraksiyadan asılı olmasıdır. Prinsip yalnız adınım mənası kimi asılılığın istiqamətini dəyişdirmir. Aralarında bir abstraksiya təqdim edərək yüksək səviyyəli və aşağı səviyyəli modullar arasındakı asılılığı bölür. Beləliklə, sonda iki asılılıq əldə edilir:

Yüksək səviyyəli modul abstraksiya bağlıdır və
Aşağı səviyyə eyni abstraksiyaya bağlıdır.

Bu prinsipi nümayiş etdirmək üçün sadə Komputer sinifni yaradaq. Komputer istifadə üçün bizə həmçinin Monitor və StandartKlaviatura sinifləridə lazım olacaq:

Komputer sinifində yeni kompüterimizi yaradırıq:

Lakin yeni Komputer sinifdə Dependency İnversion prisipi pozulmuşdur. Beləki, baza sinif Komputer daha aşağı səviyyəli siniflərdən asılıdır(e.g Monitor, StandartKlaviatura). Beləki Monitor və StandartKlaviatura sinifləri “new” keyword-dən istifadə edib yaradaraq, bu 3 sinifi bir-biri ilə sıx əlaqə yaradaraq birləşdirik. Bununla, yalnız Komputer sinifi sınaqdan keçirməkdə çətinlik çəkmiyəcəyik, eyni zamanda ehtiyac yaranacağı təqdirdə StandartKlavaitura sinifini fərqli bir ilə dəyişdirmək imkanını itirdik. Həm də Monitor sinifimizlə də eyni problemi yaşayacağıq. Sinifi sadələşdirmək üçün Klaviatura addında daha general interface əlavə edək və StandartKlaviatura sinifni Klaviaturadan implement edək.

Klaviatura asılılığını Kompyuter sinifinə əlavə etməyi asanlaşdırmaq üçün burada dependency injection pattern istifadə edirik.

Beləliklə proqramımızda heçbir yuxarı səviyyə və aşağı səviyyə modullar arasında birbaşa əlaqə yoxur, bunun əvəzində interface vasitəsi ilə aşağı səviyyə modulu yuxarı səviyyə modul ilə dependency injection pattern köməkliyi ilə əlaqələndiririk.

Bu məqalədə bacarığım qədər SOLID prinsiplər nədir və necə işləyir suallarına aydınlığ gətirməyə çalışdım. İstifadə edilən kodları Github-da görə bilərsiniz.