Natrag na blog

SOLID: Prvih 5 principa objektno orijentiranog dizajna?

SOLID: Prvih 5 principa objektno orijentiranog dizajna?

Uvod

SOLID je mnemonički akronim za pet načela objektno orijentiranog dizajna koje je uveo Robert C. Martin, popularno poznat kao Uncle Bob. Ova načela osmišljena su kako bi pomogla dizajnerima softvera, arhitektima, inženjerima i programerima u stvaranju fleksibilnijeg softvera koji je lakše održavati i skalirati. Slijedeći ova načela, možete dizajnirati klase koje je lakše testirati, refaktorirati, ponovno upotrijebiti i proširiti.

Akronim SOLID označava:

S – Načelo jedinstvene odgovornosti

O – Načelo otvorenosti/zatvorenosti

L – Načelo Liskovljeve zamjene

I – Načelo segregacije sučelja

D – Načelo inverzije ovisnosti

U ovom članku objasnit ćemo svako načelo pojedinačno kako bismo razumjeli kako vam može pomoći u pisanju boljeg koda. Osim toga, dodat ćemo isječke koda za svako načelo kako bismo vam pokazali kako ih možete primijeniti u svom programiranju, kao i što biste trebali izbjegavati u arhitekturi čistog koda. Za demonstraciju koncepata koristit ćemo programski jezik Kotlin koji su razvili JetBrains i suradnici otvorenog koda.

Načelo jedinstvene odgovornosti

Načelo jedinstvene odgovornosti (SRP) je načelo dizajna softvera koje nalaže da svaka klasa ili modul u programu treba imati jedinstvenu, dobro definiranu odgovornost. To znači da bi klasa trebala imati samo jedan razlog za promjenu i trebala bi biti odgovorna za samo jedan dio funkcionalnosti programa.

Razmotrite sljedeći isječak koda kao primjer kako se ovo načelo može primijeniti u jeziku Kotlin:

Ključna riječ data  u Kotlinu označava da je ova klasa podatkovna klasa, što znači da je namijenjena pohrani podataka i nema nikakvo složeno ponašanje. Koristeći ovu podatkovnu klasu, možemo stvoriti UserService  klasu za upravljanje korisnicima kao što je prikazano u nastavku:

U ovom primjeru, klasa UserService ima jedinstvenu odgovornost: upravljanje korisnicima u bazi podataka. Svaki korisnik je predstavljen s data class User  kodom koji je ranije podijeljen. Svi metodi u klasi UserService povezani su s ovom odgovornošću i stoga su kohezivni. To olakšava razumijevanje i održavanje klase, jer je jasno da su svi metodi u klasi povezani s jednim, dobro definiranim zadatkom.

Metoda koja krši Načelo jedinstvene odgovornosti (SRP) u klasi UserService bila bi ona koja nije povezana s primarnom odgovornošću klase, a to je upravljanje korisnicima u bazi podataka. Na primjer, razmotrite sljedeću varijaciju klase UserService :

U ovom primjeru, sendEmail  metoda ne odnosi se na primarnu odgovornost UserService klase, a to je upravljanje korisnicima u bazi podataka. Ova metoda je odgovorna za slanje e-pošte, što je zasebna briga od upravljanja korisnicima. Kao rezultat toga, ova metoda krši SRP, jer uvodi drugi razlog za promjenu klase UserService.

Kako bismo se pridržavali načela jedinstvene odgovornosti, bilo bi bolje odvojiti funkcionalnost slanja e-pošte u zasebnu klasu, kao što je EmailService  klasa. To bi omogućilo klasi UserService da se usredotoči na svoju primarnu odgovornost upravljanja korisnicima, a klasi EmailService da se usredotoči na svoju odgovornost slanja e-pošte.

Treba napomenuti da se načelo jedinstvene odgovornosti ne odnosi na broj metoda koje klasa ima, već na kohezivnost metoda i jasno razdvajanje odgovornosti unutar klase.

Načelo otvorenosti/zatvorenosti

Načelo otvorenosti/zatvorenosti (OCP) je načelo dizajna softvera koje navodi da bi softverski entiteti (kao što su klase, moduli ili funkcije) trebali biti otvoreni za proširenje, ali zatvoreni za izmjene. To znači da bi trebalo biti moguće dodati novu funkcionalnost klasi ili modulu bez mijenjanja njezinog postojećeg koda.

Razmotrimo klasu UserService iz prethodnog primjera. Pretpostavimo da želimo dodati novu značajku u klasu UserService koja nam omogućuje pretraživanje korisnika po adresi e-pošte. Jedan od načina da to učinimo bio bi dodavanje nove metode u klasu UserService kao što je istaknuto u kodu u nastavku:

Ovaj pristup radi, ali krši načelo otvorenosti/zatvorenosti, jer smo morali modificirati postojeću klasu UserService kako bismo dodali novu značajku. Bolji pristup bio bi korištenje nasljeđivanja ili kompozicije za proširenje funkcionalnosti klase UserService bez mijenjanja njezinog koda.

Kako bismo to postigli, mogli bismo kreirati novu klasu pod nazivom UserSearchService  koja proširuje klasu UserService i dodaje funkcionalnost pretraživanja po e-pošti:

U ovom primjeru, klasa UserSearchService otvorena je za proširenje, jer pruža dodatnu funkcionalnost izvan onoga što nudi klasa UserService klasa. Istovremeno, UserService klasa ostaje zatvorena za izmjene, jer nismo morali mijenjati njezin kod kako bismo dodali značajku pretraživanja po e-pošti.

Liskovljev princip zamjene

Liskovljev princip zamjene (LSP) je princip dizajna softvera koji navodi da bi objekti nadklase trebali moći biti zamijenjeni objektima podklase bez utjecaja na ispravnost programa. To znači da bi podklasa trebala biti valjana zamjena za svoju nadklasu i trebala bi se ponašati na isti način kao i nadklasa kada se koristi u istom kontekstu.

Nastavit ćemo demonstraciju koristeći User i UserService klase iz prethodnih primjera. Kako bismo omogućili proširenje klase User, koristimo ključnu riječ open  u Kotlinu:

Ovdje je originalna UserService klasa koja koristi User podatkovnu klasu iznad:

Pretpostavimo da želimo kreirati podklasu od User pod nazivom AdminUser koja predstavlja korisnike s administrativnim privilegijama. To bismo mogli učiniti ovako:

U ovom primjeru, klasa AdminUser valjana je zamjena za klasu User jer se ponaša na isti način kao i klasa User i može se koristiti svugdje gdje se očekuje User objekt. Na primjer, možemo koristiti klasu AdminUser s klasom UserService  na ovaj način:

Ovaj kod je ispravan, jer je klasa AdminUser valjana zamjena za klasu User i može se koristiti na isti način kao i User objekt.

Važno je napomenuti da se Liskovljev princip zamjene ne odnosi samo na nasljeđivanje. Radi se o osiguravanju da se objekti podklase ponašaju na isti način kao i objekti nadklase, bez obzira na to kako je podklasa implementirana. Na primjer, kada bi se klasa AdminUser ponašala drugačije od klase User na neki način, to bi prekršilo Liskovljev princip zamjene, jer ne bi bila valjana zamjena za klasu User klasu.

Princip segregacije sučelja

Princip segregacije sučelja (ISP) je princip dizajna softvera koji navodi da klijenti ne bi trebali biti prisiljeni ovisiti o sučeljimakoja ne koriste. To znači da je općenito dobra ideja kreirati mala, fokusirana sučelja koja dobro rade jednu stvar, umjesto kreiranja velikih sučelja koja pokušavaju raditi mnogo stvari.

Evo primjera kako se ovaj princip može primijeniti ponovnim pisanjem User  i UserService  klasa iz prethodnih primjera:

U ovom primjeru, UserService sučelje definira četiri metode koje su povezane s upravljanjem korisnicima u bazi podataka. DatabaseUserService  klasa implementira ovo sučelje i pruža konkretne implementacije za ove metode.

Pretpostavimo da želimo dodati novu značajku u UserService sučelje koje nam omogućuje pretraživanje korisnika po adresi e-pošte. Jedan od načina da to učinimo bio bi dodavanje nove metode u UserService sučelje:

Vaš se kod neće pokrenuti, osim ako također ne implementirate ovu metodu u svim klasama koje implementiraju UserService sučelje:

Iako ovaj pristup radi, on krši princip segregacije sučelja (Interface Segregation Principle), jer prisiljava DatabaseUserService  klasu da implementira metodu ( searchUsersByEmail ) koja joj možda nije potrebna ili je ne koristi.

Bolji pristup bio bi stvaranje zasebnog sučelja  za funkcionalnost pretraživanja po e-pošti:

Sada imamo zasebna, mala i usmjerena sučelja, tj. UserService  i UserSearchServicekoja imaju jedinstvenu odgovornost. Klasa koja zahtijeva sve funkcionalnosti ovih sučelja može ih implementirati kao što je prikazano u isječku koda u nastavku:

To je u skladu s načelom segregacije sučelja, jer osigurava da klijenti (kao što je DatabaseUserService klasa) nisu prisiljeni ovisiti o sučeljima koja ne koriste.

Kako bismo bolje razumjeli ovaj koncept, pretpostavimo da imamo drugu klasu pod nazivom MemoryUserService  koja implementira sučelje UserService  ali ne treba funkcionalnost pretraživanja po e-pošti, kod možemo napisati ovako:

U ovom primjeru, klasa MemoryUserService treba samo implementirati metode definirane u sučelju UserService i ne mora brinuti o funkcionalnosti pretraživanja po e-pošti. To omogućuje klasi MemoryUserService da se usredotoči na svoju primarnu odgovornost upravljanja korisnicima u memoriji, umjesto da bude prisiljena implementirati nepovezanu funkcionalnost.

Načelo inverzije ovisnosti (Dependency Inversion Principle)

Načelo inverzije ovisnosti (DIP) je načelo dizajna softvera koje navodi da moduli visoke razine ne bi trebali ovisiti o modulima niske razine, već bi oboje trebali ovisiti o apstrakcijama. To znači da je općenito dobra ideja dizajnirati softver tako da komponente visoke razine nisu vezane uz specifične implementacije komponenti niske razine, već ovise o apstrakcijama (kao što su sučelja ili apstraktne klase) koje se mogu implementirati na različite načine.

Pogledajmo primjer kako se ovo načelo može primijeniti na klase User i UserService korištene u prethodnim isječcima koda:

U ovom primjeru, UserService klasa ovisi o apstrakciji zvanoj UserRepository sučelje, umjesto da ovisi o specifičnoj implementaciji repozitorija korisnika. To nam omogućuje implementaciju UserRepository sučelja na različite načine, primjerice s bazom podataka ili pohranom u memoriji, bez utjecaja na UserService klasu.

Na primjer, evo implementacije UserRepository sučelja koje koristi bazu podataka:

Evo još jedne implementacije UserRepository sučelja koje koristi pohranu u memoriji:

To čini sustav fleksibilnijim i lakšim za održavanje, jer nam omogućuje promjenu implementacije repozitorija bez utjecaja na ostatak sustava. Također olakšava testiranje klase UserService, jer možemo simulirati ovisnost UserRepository u našim testovima.

Zaključak

U ovom smo članku govorili o pet načela SOLID  kod i dijeljene isječke koda koji zadovoljavaju svako načelo. Pridržavanje ovih načela može vam pomoći u dizajniranju softverskih sustava koji su fleksibilniji, lakši za održavanje i skalabilniji. Međutim, važno je imati na umu da su ova načela smjernice, a ne stroga pravila, te je na razvojnom programeru da odluči kada će ih i kako primijeniti u kontekstu svog specifičnog projekta. Nastavite učiti posjetom našem blogu za detaljnije i ažurnije članke i vodiče o računalstvu u oblaku i DevOpsu, dizajnu i razvoju softvera, tehnološkim trendovima na koje treba obratiti pozornost i još mnogo toga.

Sretno kodiranje!

author

Preslav Dobrev

Autor · CloudSigma

Preslav Dobrev je kreativni dizajner u CloudSigma, usredotočen na dosljedan poslovni identitet korištenjem tradicionalnih i inovativnih marketinških kanala. Vješt je u spajanju umjetničke vizije sa strateškim marketingom kako bi stvorio dojmljive brendirane priče.

Komentari

Još nema komentara. Budite prvi.