Sve što trebate znati o čvrstim načelima u Javi



U ovom ćete članku detaljno naučiti što su Solid principi u javi s primjerima i njihovu važnost u stvarnom životu.

U svijetu (OOP), postoje mnoge smjernice, obrasci ili principi za dizajn. Pet od ovih načela obično se grupiraju zajedno i poznati su pod kraticom ČVRSTO. Iako svako od ovih pet načela opisuje nešto specifično, oni se preklapaju tako da usvajanje jednog od njih implicira ili dovodi do usvajanja drugog. U ovom ćemo članku razumjeti ČVRSTE principe u Javi.

Povijest ČVRSTIH principa na Javi

Robert C. Martin dao je pet objektno orijentiranih principa dizajna, a za njega se koristi kratica 'S.O.L.I.D'. Kada kombinirate sve principe S.O.L.I.D, postaje vam lakše razviti softver kojim se lako može upravljati. Ostale značajke korištenja S.O.L.I.D su:





  • Izbjegava mirise koda.
  • Brzo kod refraktora.
  • Može prilagodljivi ili agilni razvoj softvera.

Kada u kodiranju koristite princip S.O.L.I.D, započinjete s pisanjem koda koji je i učinkovit i djelotvoran.



c ++ ide sortirati

Koje je značenje S.O.L.I.D?

Solid predstavlja pet principa Java-a koji su:

  • S : Načelo jedinstvene odgovornosti
  • ILI : Načelo otvoreno-zatvoreno
  • L : Liskov princip zamjene
  • Ja : Načelo segregacije sučelja
  • D : Načelo inverzije ovisnosti

Na ovom ćemo blogu detaljno razgovarati o svih pet SOLID principa Jave.



Načelo pojedinačne odgovornosti u Javi

Što kaže?

Robert C. Martin to opisuje kako bi jedan razred trebao imati samo jednu i jedinu odgovornost.

Prema principu jedinstvene odgovornosti, trebao bi postojati samo jedan razlog zbog kojeg se klasa mora mijenjati. To znači da bi razred trebao imati jedan zadatak. Ovo se načelo često naziva subjektivnim.

Načelo se može dobro razumjeti na primjeru. Zamislite da postoji klasa koja izvodi sljedeće operacije.

  • Povezano s bazom podataka

  • Pročitajte neke podatke iz tablica baze podataka

  • Napokon, zapišite ga u datoteku.

Jeste li zamislili scenarij? Ovdje klasa ima više razloga za promjenu, a malo je njih modifikacija izlaza datoteke, usvajanje nove baze podataka. Kad govorimo o odgovornosti s jednim principom, rekli bismo, previše je razloga da se klasa mijenja, stoga se ne uklapa pravilno u načelo jedinstvene odgovornosti.

Na primjer, klasa Automobile može se sama pokrenuti ili zaustaviti, ali zadatak pranja pripada klasi CarWash. U drugom primjeru, klasa Book ima svojstva za pohranu vlastitog imena i teksta. No zadatak tiskanja knjige mora pripadati klasi Printer knjiga. Klasa Book Printer možda se ispisuje na konzolu ili neki drugi medij, ali takve ovisnosti uklanjaju se iz klase Book

Zašto je potreban ovaj princip?

Kada se slijedi princip jedinstvene odgovornosti, testiranje je lakše. S jednom odgovornošću, razred će imati manje test slučajeva. Manje funkcionalnosti također znači i manje ovisnosti o drugim klasama. To dovodi do bolje organizacije koda, jer je manje i dobro namijenjene klase lakše pretraživati.

Primjer za pojašnjenje ovog načela:

Pretpostavimo da se od vas traži da implementirate uslugu UserSetting u kojoj korisnik može promijeniti postavke, ali prije toga mora biti autentificiran. Jedan od načina da se to primijeni bio bi:

javna klasa UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// Dozvola za promjenu}} public boolean checkAccess (User user) {// Provjerite je li korisnik valjan. }}

Sve izgleda dobro dok ne želite ponovno upotrijebiti kod checkAccess na nekom drugom mjestu ILI želite izmijeniti način na koji se vrši checkAccess. U sva dva slučaja na kraju biste promijenili istu klasu, a u prvom biste slučaju morali koristiti UserSettingService i za provjeru pristupa.
Jedan od načina da se to ispravi je dekompozicija UserSettingService na UserSettingService i SecurityService. I premjestite kod checkAccess u SecurityService.

javna klasa UserSettingService {public void changeEmail (User user) {if (SecurityService.checkAccess (user)) {// Odobri mogućnost promjene}}} public class SecurityService {public static boolean checkAccess (User user) {// provjeri pristup. }}

Otvorite zatvoreni princip u Javi

Robert C. Martin to opisuje kao softverske komponente koje bi trebale biti otvorene za proširenje, ali zatvorene za izmjene.

Točnije, prema ovom načelu, klasa bi trebala biti napisana na takav način da svoj posao obavlja besprijekorno bez pretpostavke da će ljudi u budućnosti jednostavno doći i promijeniti je. Stoga bi klasa trebala ostati zatvorena radi modifikacije, ali trebala bi imati mogućnost proširenja. Načini proširenja nastave uključuju:

  • Nasljeđivanje iz razreda

  • Prepisivanje potrebnog ponašanja iz razreda

  • Proširivanje određenih ponašanja razreda

Izvrsni primjer principa otvoreno-zatvoreno može se razumjeti uz pomoć preglednika. Sjećate li se instaliranja proširenja u Chrome preglednik?

Osnovna funkcija Chrome preglednika je surfanje različitim web mjestima. Želite li provjeriti gramatiku kada pišete e-poštu pomoću Chrome preglednika? Ako da, možete jednostavno koristiti proširenje Grammarly, pruža vam provjeru gramatike na sadržaju.

Ovaj mehanizam u koji dodajete stvari za povećanje funkcionalnosti preglednika je proširenje. Stoga je preglednik savršen primjer funkcionalnosti koja je otvorena za proširenje, ali je zatvorena za izmjene. Jednostavnim riječima, možete poboljšati funkcionalnost dodavanjem / instaliranjem dodataka u svoj preglednik, ali ne možete izgraditi ništa novo.

Zašto je to načelo potrebno?

OCP je važan jer nam predavanja mogu dolaziti putem neovisnih knjižnica. Morali bismo biti u mogućnosti proširiti te razrede bez brige mogu li te osnovne klase podržati naša proširenja. Ali nasljeđivanje može dovesti do potklasa koje ovise o implementaciji osnovne klase. Da bi se to izbjeglo, preporučuje se korištenje sučelja. Ova dodatna apstrakcija dovodi do labavog spajanja.

Recimo da moramo izračunati površine raznih oblika. Počinjemo s izradom klase za naš prvi oblik pravokutnikakoji ima 2 atributa duljine& širina.

javni razred Pravokutnik {javna dvostruka duljina javna dvostruka širina}

Dalje kreiramo klasu za izračunavanje površine ovog pravokutnikakoja ima metodu izračunatiRectangleAreakoji uzima Pravokutnikkao ulazni parametar i izračunava njegovu površinu.

javna klasa AreaCalculator {javno dvostruko izračunavanjeRectangleArea (pravokutnik pravokutnika) {povratak pravokutnika.duljina * pravokutnik.širina}}

Zasada je dobro. Sad recimo da dobijemo svoj drugi oblik kruga. Tako odmah izrađujemo novi razredni krugs jednim polumjerom atributa.

krug javne klase {javni dvostruki radijus}

Zatim modificiramo Areacalculatorklasa za dodavanje izračuna kružnica novom metodom calcuCircleaArea ()

javna klasa AreaCalculator {javni dvostruki izračunRectangleArea (pravokutnik pravokutnika) {povratak pravokutnika.dužina * pravokutnik.širokost} javni dvostruki izračunCircleArea (krug kruga) {povratak (22/7) * krug.radius * krug.radius}}

Međutim, imajte na umu da je u načinu na koji smo gore dizajnirali svoje rješenje bilo nedostataka.

Recimo da imamo novi oblik peterokuta. U tom ćemo slučaju opet završiti s modificiranjem klase AreaCalculator. Kako vrste oblika rastu, to postaje sve zamršenije kako se AreaCalculator neprestano mijenja, a svi potrošači ove klase morat će nastaviti ažurirati svoje knjižnice koje sadrže AreaCalculator. Kao rezultat toga, klasa AreaCalculator neće biti sa sigurnošću osnovana (finalizirana), jer će svaki put kada dođe novi oblik biti izmijenjena. Dakle, ovaj dizajn nije zatvoren za izmjene.

AreaCalculator morat će nastaviti dodavati svoju računsku logiku u novije metode. Zapravo ne širimo opseg oblika, nego jednostavno radimo rješenje po dijelovima (malo po malo) za svaki dodani oblik.

Izmjena gornjeg dizajna u skladu s načelom otvoreno / zatvoreno:

Pogledajmo sada elegantniji dizajn koji rješava nedostatke gore navedenog dizajna pridržavajući se otvorenog / zatvorenog načela. Prije svega učinit ćemo dizajn proširivim. Za to prvo moramo definirati osnovni tip Shape i imati krug i pravokutnik koji implementira sučelje oblika.

javno sučelje Oblik {javna dvostruka kalkulacijaArea ()} javna klasa Pravokutnik implementira Oblik {dvostruka dužina dvostruka širina javna dvostruka računArea () {povratna dužina * širina}} javna klasa Krug implementira Oblik {javna dvostruka radijusa javna dvostruka izračunajArea () {return (22 / 7) * polumjer * polumjer}}

Postoji osnovni oblik sučelja. Svi oblici sada implementiraju osnovni oblik sučelja. Sučelje oblika ima apstraktnu metodu calcuArea (). I krug i pravokutnik pružaju vlastitu nadjačanu provedbu metode CalcuArea () koristeći vlastite atribute.
Donijeli smo stupanj proširivosti jer su oblici sada instanca sučelja oblika. To nam omogućuje upotrebu Shape umjesto pojedinih klasa
Posljednja točka gore spomenutog potrošača ovih oblika. U našem slučaju potrošač će biti klasa AreaCalculator koja bi sada izgledala ovako.

javna klasa AreaCalculator {javni dvostruki izračunShapeArea (Oblik oblika) {return shape.calculateArea ()}}

Ovaj kalkulator područjaclass sada u potpunosti uklanja naše gore navedene nedostatke u dizajnu i daje čisto rješenje koje se pridržava Otvoreno-zatvorenog načela. Krenimo s ostalim ČVRSTIM principima u Javi

Zamjenski princip Liskova u Javi

Robert C. Martin to opisuje kao izvedeni tipovi moraju biti u potpunosti zamjenjivi za svoje osnovne tipove.

Liskov princip supstitucije pretpostavlja da je q (x) svojstvo, dokazivo o entitetima x koji pripada tipu T. Sada, prema ovom principu, q (y) bi sada trebao biti dokaziv za objekte y koji pripadaju tipu S, i S je zapravo podvrsta T. Jeste li sada zbunjeni i ne znate što zapravo znači Liskov princip supstitucije? Definicija bi mogla biti pomalo složena, ali zapravo je prilično jednostavna. Jedina stvar je da bi svaka podrazred ili izvedena klasa trebala biti zamjenjiva za svoju roditeljsku ili osnovnu klasu.

Možete reći da je to jedinstveni objektno orijentirani princip. Načelo može dalje pojednostaviti dječji tip određenog tipa roditelja, bez kompliciranja ili dizanja stvari u zrak, što bi trebalo biti sposobno zauzeti se za tog roditelja. Ovo je načelo usko povezano s načelom zamjene Liskova.

Zašto je to načelo potrebno?

Time se izbjegava zlouporaba nasljedstva. Pomaže nam da se prilagodimo odnosu „je-a“. Također možemo reći da potklase moraju ispunjavati ugovor definiran osnovnom klasom. U tom je smislu povezano saDizajn po ugovoruto je prvi opisao Bertrand Meyer. Na primjer, primamljivo je reći da je krug vrsta elipse, ali krugovi nemaju dva žarišta ili glavnu / molu os.

LSP se popularno objašnjava na primjeru kvadrata i pravokutnika. ako pretpostavimo ISA odnos između kvadrata i pravokutnika. Stoga nazivamo 'Kvadrat je pravokutnik'. Kôd u nastavku predstavlja odnos.

javna klasa Pravokutnik {private int length private int widthth public int getLength () {return length} javna praznina setLength (int length) {this.length = length} public int getBreadth () {return widthth} javna praznina setBreadth (int širina) { this.breadth = širina} public int getArea () {return this.length * this.breadth}}

Ispod je kod za Square. Imajte na umu da se kvadrat proteže pravokutnik.

javna klasa Square proteže se Pravokutnik {javna praznina setBreadth (int širina) {super.setBreadth (širina) super.setLength (širina)} javna praznina setLength (int dužina) {super.setLength (dužina) super.setBreadth (dužina)}}

U ovom slučaju, pokušavamo uspostaviti ISA odnos između kvadrata i pravokutnika tako da bi pozivanje 'Square je pravokutnik' u donjem kodu počelo neočekivano ponašanje ako se proslijedi instanca kvadrata. Pogreška u tvrdnji izbacit će se u slučaju provjere za 'Područje' i provjere za 'Širinu', iako će program završiti jer se pogreška u tvrdnji baci zbog neuspjeha provjere Područja.

javna klasa LSPDemo {javna praznina izračunaj površinu (pravokutnik r) {r.setBreadth (2) r.setLength (3) potvrdi r.getArea () == 6: printError ('area', r) assert r.getLength () == 3: printError ('length', r) assert r.getBreadth () == 2: printError ('width', r)} private String printError (String errorIdentifer, Rectangle r) {return 'Neočekivana vrijednost' + errorIdentifer + ' na primjer '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // Primjer pravokutnika prosljeđuje se lsp.calculateArea (novi pravokutnik ()) // Donosi se instanca kvadrata lsp.calculateArea (novi kvadrat ())}}

Razred pokazuje princip zamjene Liskova (LSP) Prema principu, funkcije koje koriste reference na osnovne klase moraju moći koristiti objekte izvedene klase, a da to ne znaju.

Dakle, u primjeru prikazanom dolje, funkcija calcuArea koja koristi referencu 'Pravokutnik' trebala bi moći koristiti objekte izvedene klase kao što je Square i ispuniti zahtjev postavljen definicijom pravokutnika. Treba imati na umu da prema definiciji pravokutnika, slijedeći podaci moraju uvijek biti istiniti s obzirom na donje podatke:

  1. Duljina mora uvijek biti jednaka duljini koja se prosljeđuje kao ulaz u metodu, setLength
  2. Širina mora uvijek biti jednaka širini koja je proslijeđena kao ulaz u metodu, setBreadth
  3. Površina mora uvijek biti jednaka proizvodu duljine i širine

U slučaju da pokušamo uspostaviti ISA odnos između kvadrata i pravokutnika tako da zovemo 'kvadrat je pravokutnik', gornji kod bi se počeo neočekivano ponašati ako se proslijedi instanca kvadrata. U slučaju provjere površine i provjere pojavit će se pogreška u tvrdnji za širinu, iako će program završiti jer se pogreška tvrdnje izbaci zbog neuspjeha provjere područja.

Klasa Square ne trebaju metode poput setBreadth ili setLength. Klasa LSPDemo trebala bi znati detalje izvedenih klasa Rectangle (kao što je Square) kako bi ih prikladno kodirala kako bi se izbjegla pogreška u bacanju. Promjena postojećeg koda prije svega narušava načelo otvoreno-zatvoreno.

Načelo segregacije sučelja

Robert C. Martin to opisuje jer klijente ne treba prisiljavati na primjenu nepotrebnih metoda koje neće koristiti.

PremaNačelo segregacije sučeljaklijenta, bez obzira na to što nikada ne smije biti prisiljeno implementirati sučelje koje ne koristi ili klijent nikada ne bi trebao biti obvezan ovisiti o bilo kojoj metodi koju oni ne koriste. Dakle, u osnovi, principi segregacije sučelja kako želite sučelja, koja su mala, ali specifična za klijenta, umjesto monolitnog i većeg sučelja. Ukratko, bilo bi loše da natjerate klijenta da ovisi o određenoj stvari, koja im nije potrebna.

Na primjer, jedno sučelje za bilježenje za pisanje i čitanje dnevnika korisno je za bazu podataka, ali ne i za konzolu. Čitanje dnevnika nema smisla za zapisnik konzole. Nastavljajući s ovim člankom SOLID Principles in Java.

Zašto je to načelo potrebno?

Recimo da postoji sučelje restorana koje sadrži metode za prihvaćanje narudžbi internetskih kupaca, kupaca putem telefonske linije i telefonskih prolaznika i korisnika koji ulaze. Sadrži i metode rukovanja internetskim plaćanjima (za mrežne kupce) i osobnim plaćanjima (za kupce koji ulaze i telefonske kupce kada im se narudžba isporučuje kod kuće).

Ajmo sada stvoriti Java sučelje za restoran i nazvati ga RestaurantInterface.java.

javno sučelje RestaurantInterface {javna praznina acceptOnlineOrder () javna praznina takeTelephoneOrder () javna praznina payOnline () javna praznina walkInCustomerOrder () javna praznina payInPerson ()}

U RestaurantInterfaceu definirano je 5 metoda za prihvaćanje internetske narudžbe, preuzimanje telefonske narudžbe, prihvaćanje narudžbi od korisnika koji ulaze, prihvaćanje internetskog plaćanja i osobno prihvaćanje plaćanja.

Krenimo od implementacije RestaurantInterface za mrežne kupce kao OnlineClientImpl.java

javna klasa OnlineClientImpl implementira RestaurantInterface {public void acceptOnlineOrder () {// logika za postavljanje mrežne narudžbe} public void takeTelephoneOrder () {// Nije primjenjivo za internetsku narudžbu bacati novu UnsupportedOperationException ()} javnu void payOnline () {// logiku za plaćanje online} javna void walkInCustomerOrder () {// Nije primjenjivo za internetsku narudžbu bacanje novog UnsupportedOperationException ()} javna void payInPerson () {// nije primjenjivo za internetsku narudžbu bacanje nove UnsupportedOperationException ()}}
  • Budući da je gornji kod (OnlineClientImpl.java) namijenjen mrežnim narudžbama, bacite UnsupportedOperationException.

  • Mrežni, telefonski i walk-in klijenti koriste izvedbu RestaurantInterface specifičnu za svakog od njih.

  • Klase implementacije za telefonskog klijenta i walk-in klijenta imat će nepodržane metode.

  • Budući da je 5 metoda dio RestaurantInterface-a, klase implementacije moraju implementirati svih 5.

  • Metode koje svaka od klasa implementacije baca UnsupportedOperationException. Kao što jasno možete vidjeti - provedba svih metoda je neučinkovita.

  • Svaka promjena bilo koje metode Restoranskog sučelja proširit će se na sve klase implementacije. Održavanje koda tada postaje stvarno glomazno i ​​regresijski učinci promjena i dalje će se povećavati.

  • RestaurantInterface.java krši načelo pojedinačne odgovornosti jer su logika plaćanja i logika za narudžbine grupirane u jednom sučelju.

Da bismo prevladali gore spomenute probleme, primjenjujemo Princip segregacije sučelja za refaktoriranje gornjeg dizajna.

  1. Odvojite funkcionalnosti plaćanja i narudžbe u dva odvojena vitka sučelja, PaymentInterface.java i OrderInterface.java.

  2. Svaki od klijenata koristi po jednu implementaciju PaymentInterface i OrderInterface. Na primjer - OnlineClient.java koristi OnlinePaymentImpl i OnlineOrderImpl i tako dalje.

  3. Načelo pojedinačne odgovornosti sada je priloženo kao sučelje za plaćanje (PaymentInterface.java) i sučelje za naručivanje (OrderInterface).

  4. Promjena bilo kojeg sučelja naloga ili plaćanja ne utječe na drugo. Sada su neovisni. Neće biti potrebno raditi bilo kakvu lažnu implementaciju ili bacati UnsupportedOperationException, jer svako sučelje ima samo metode koje će uvijek koristiti.

Nakon primjene ISP-a

Načelo inverzije ovisnosti

Robert C. Martin to opisuje jer ovisi o apstrakcijama, a ne o konkrementima. Prema njemu, modul visoke razine nikada se ne smije oslanjati ni na jedan modul niske razine. na primjer

Otiđite u lokalnu trgovinu da nešto kupite i odlučite to platiti debitnom karticom. Dakle, kada kartici date službenicu za izvršenje plaćanja, službenik se ne trudi provjeriti kakvu ste karticu dali.

Čak i ako ste dali Visa karticu, on neće ugasiti Visa uređaj za prevlačenje vaše kartice. Vrsta kreditne ili debitne kartice koju plaćate nije ni bitna jer će je jednostavno prevući. Dakle, u ovom primjeru možete vidjeti da i vi i službenik ovisite o apstrakciji kreditne kartice i da vas ne brinu specifičnosti kartice. To je ono što je princip inverzije ovisnosti.

Zašto je to načelo potrebno?

Omogućuje programeru da ukloni tvrdo kodirane ovisnosti, tako da aplikacija postaje labavo povezana i proširiva.

javni razred student {privatna adresa Adresa public Student () {adresa = nova adresa ()}}

U gornjem primjeru klasa Student zahtijeva objekt Address i odgovoran je za inicijalizaciju i upotrebu objekta Address. Ako se u budućnosti promijeni adresa, tada moramo izvršiti promjene i u klasi učenika. To čini usku povezanost između predmeta Student i Address. Ovaj problem možemo riješiti pomoću uzorka dizajna inverzije ovisnosti. tj. Objekt adrese implementirat će se neovisno i pružit će se studentu kada se student instancira pomoću inverzije ovisnosti na temelju konstruktora ili setera.

Ovim smo došli do kraja ovih ČVRSTIH principa u Javi.

Pogledajte Edureka, pouzdana tvrtka za internetsko učenje s mrežom od više od 250 000 zadovoljnih učenika raširenih širom svijeta. Edurekin tečaj za obuku i certificiranje Java J2EE i SOA namijenjen je studentima i profesionalcima koji žele biti programer Java. Tečaj je dizajniran da vam pruži početnu prednost u Java programiranju i osposobi vas za osnovne i napredne Java koncepte zajedno s raznim Java okvirima poput Hibernate & Spring.

Imate pitanje za nas? Molimo vas da ga spominjete u odjeljku za komentare ovog bloga „ČVRSTI principi u Javi“, a mi ćemo vam se javiti u najkraćem mogućem roku.