Alt du trenger å vite om solide prinsipper i Java



I denne artikkelen vil du lære i detalj om hva som er solide prinsipper i java med eksempler og deres betydning med virkelighetseksempel.

I verden av (OOP), det er mange designretningslinjer, mønstre eller prinsipper. Fem av disse prinsippene er vanligvis gruppert sammen og er kjent under forkortelsen SOLID. Mens hvert av disse fem prinsippene beskriver noe spesifikt, overlapper de også slik at vedtakelse av en av dem innebærer eller fører til å adoptere en annen. I denne artikkelen vil vi forstå SOLID-prinsipper i Java.

Historien om SOLID-prinsipper i Java

Robert C. Martin ga fem objektorienterte designprinsipper, og akronymet 'S.O.L.I.D' brukes til det. Når du bruker alle prinsippene til S.O.L.I.D på en kombinert måte, blir det lettere for deg å utvikle programvare som lett kan administreres. De andre funksjonene ved bruk av S.O.L.I.D er:





  • Det unngår kodelukt.
  • Rask refraktorkode.
  • Kan gjøre adaptiv eller smidig programvareutvikling.

Når du bruker prinsippet til S.O.L.I.D i kodingen din, begynner du å skrive koden som er både effektiv og effektiv.



Hva er betydningen av S.O.L.I.D?

Solid representerer fem prinsipper for java som er:

  • S : Enkeltansvarsprinsipp
  • ELLER : Åpen-lukket prinsipp
  • L : Liskov-substitusjonsprinsipp
  • Jeg : Grensesnitt segregeringsprinsipp
  • D : Avhengighetsinversjonsprinsipp

I denne bloggen vil vi diskutere alle de fem SOLID-prinsippene til Java i detalj.



Prinsipp om enkeltansvar i Java

Hva står det?

Robert C. Martin beskriver det som at en klasse bare skal ha et eneste ansvar.

I henhold til prinsippet om enkeltansvar bør det bare være en grunn som en klasse må endres til. Det betyr at en klasse skal ha en oppgave å gjøre. Dette prinsippet blir ofte betegnet som subjektivt.

Prinsippet kan forstås godt med et eksempel. Tenk deg at det er en klasse som utfører følgende operasjoner.

  • Koblet til en database

  • Les noen data fra databasetabeller

  • Til slutt, skriv det til en fil.

Har du sett for deg scenariet? Her har klassen flere grunner til å endre, og få av dem er endring av filutdata, ny vedtakelse av databaser. Når vi snakker om enkeltprinsippansvar, vil vi si at det er for mange grunner til at klassen kan endres, det passer ikke riktig i prinsippet om enkeltansvar.

For eksempel kan en Automobile-klasse starte eller stoppe seg selv, men oppgaven med å vaske den tilhører CarWash-klassen. I et annet eksempel har en bokklasse egenskaper for å lagre eget navn og tekst. Men oppgaven med å trykke boken må tilhøre klassen Book Printer. Bokskriverklassen kan skrive ut på konsoll eller et annet medium, men slike avhengigheter fjernes fra bokklassen

Hvorfor er dette prinsippet påkrevd?

Når prinsippet om enkeltansvar følges, er testing lettere. Med et enkelt ansvar vil klassen ha færre testsaker. Mindre funksjonalitet betyr også færre avhengigheter til andre klasser. Det fører til bedre kodeorganisering siden mindre og velmenende klasser er lettere å søke.

Et eksempel for å avklare dette prinsippet:

Anta at du blir bedt om å implementere en UserSetting-tjeneste der brukeren kan endre innstillingene, men før det må brukeren godkjennes. En måte å implementere dette på ville være:

public class UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// Grant option to change}} public boolean checkAccess (User user) {// Bekreft om brukeren er gyldig. }}

Alt ser bra ut til du ønsker å gjenbruke checkAccess-koden på et annet sted ELLER du vil gjøre endringer i måten checkAccess utføres på. I alle to tilfeller vil du ende opp med å endre samme klasse, og i det første tilfellet må du bruke UserSettingService for å se etter tilgang også.
En måte å korrigere dette på er å dekomponere UserSettingService til UserSettingService og SecurityService. Og flytt checkAccess-koden til SecurityService.

public class UserSettingService {public void changeEmail (User user) {if (SecurityService.checkAccess (user)) {// Grant option to change}}} public class SecurityService {public static boolean checkAccess (User user) {// check the access. }}

Åpne lukket prinsipp i Java

Robert C. Martin beskriver det som programvarekomponenter skal være åpne for utvidelse, men stengt for modifikasjon.

For å være presis, i henhold til dette prinsippet, bør en klasse skrives på en slik måte at den utfører jobben sin feilfritt uten forutsetningen om at folk i fremtiden bare vil komme og endre den. Derfor bør klassen forbli stengt for modifikasjon, men den bør ha muligheten til å bli utvidet. Måter å utvide klassen inkluderer:

  • Arving fra klassen

  • Overskrive den nødvendige oppførselen fra klassen

  • Utvide visse atferd i klassen

Et utmerket eksempel på åpent lukket prinsipp kan forstås ved hjelp av nettlesere. Husker du å installere utvidelser i kromleseren din?

Grunnfunksjonen til kromleseren er å surfe på forskjellige nettsteder. Vil du sjekke grammatikk når du skriver en e-post ved hjelp av kromleser? Hvis ja, kan du bare bruke Grammarly-utvidelsen, det gir deg grammatikkjekk på innholdet.

Denne mekanismen der du legger til ting for å øke funksjonaliteten til nettleseren, er en utvidelse. Derfor er nettleseren et perfekt eksempel på funksjonalitet som er åpen for utvidelse, men som er lukket for modifikasjon. Med enkle ord kan du forbedre funksjonaliteten ved å legge til / installere plugins i nettleseren din, men kan ikke bygge noe nytt.

Hvorfor kreves dette prinsippet?

OCP er viktig siden klasser kan komme til oss gjennom tredjepartsbiblioteker. Vi burde kunne utvide disse klassene uten å bekymre oss hvis disse basisklassene kan støtte utvidelsene våre. Men arv kan føre til underklasser som er avhengig av implementering av baseklassen. For å unngå dette anbefales bruk av grensesnitt. Denne ekstra abstraksjonen fører til løs kobling.

La oss si at vi må beregne områder i forskjellige former. Vi begynner med å lage en klasse for vår første form rektangelsom har to attributter lengde& bredde.

offentlig klasse Rektangel {offentlig dobbel lengde offentlig dobbel bredde}

Deretter lager vi en klasse for å beregne arealet til dette rektangeletsom har en metode beregneRektangelAreasom tar rektangeletsom inndataparameter og beregner arealet.

offentlig klasse AreaCalculator {offentlig dobbel beregneRektangelArea (Rektangel rektangel) {retur rektangel.lengde * rektangel.bredde}}

Så langt så bra. La oss si at vi får vår andre formsirkel. Så vi oppretter raskt en ny klassesirkelmed en enkelt attributtradius.

offentlig klasse Sirkel {offentlig dobbel radius}

Deretter endrer vi Areacalculatorklasse for å legge til sirkelberegninger gjennom en ny metode, beregneCircleaArea ()

offentlig klasse AreaCalculator {offentlig dobbel beregneRektangelArea (Rektangel rektangel) {retur rektangel.lengde * rektangel.bredde} offentlig dobbel beregneCircleArea (sirkel sirkel) {retur (22/7) * sirkel.radius * sirkel.radius}}

Vær imidlertid oppmerksom på at det var feil i måten vi designet løsningen ovenfor på.

La oss si at vi har en ny femkant. I så fall vil vi igjen ende opp med å endre AreaCalculator-klassen. Etter hvert som typene vokser blir dette rotere ettersom AreaCalculator fortsetter å endre seg, og alle forbrukere i denne klassen må fortsette å oppdatere bibliotekene som inneholder AreaCalculator. Som et resultat vil ikke AreaCalculator-klassen være grunnlinjert (fullført) med sikkerhet, for hver gang en ny form kommer, vil den bli endret. Så dette designet er ikke lukket for modifikasjon.

AreaCalculator må fortsette å legge til beregningslogikken i nyere metoder. Vi utvider egentlig ikke omfanget av former, men vi gjør ganske enkelt bit-for-bit-løsning for hver form som blir lagt til.

Endring av ovennevnte design for å overholde åpnet / lukket prinsipp:

La oss nå se en mer elegant design som løser feilene i den ovennevnte designen ved å følge det åpne / lukkede prinsippet. Vi vil først og fremst gjøre designet utvidbart. For dette må vi først definere en basetype Shape og ha Circle & Rectangle implement Shape interface.

offentlig grensesnitt Form {offentlig dobbel beregneArea ()} offentlig klasse Rektangelutstyr Form {dobbel lengde dobbel bredde offentlig dobbel beregneArea () {returlengde * bredde}} offentlig klasse Sirkelredskaper Form {offentlig dobbel radius offentlig dobbel beregneArea () {retur (22 / 7) * radius * radius}}

Det er et basisgrensesnitt Shape. Alle former implementerer nå basisgrensesnittet Shape. Formgrensesnittet har en abstrakt metode for beregningArea (). Både sirkel og rektangel gir sin egen overstyrte implementering av beregningsmetoden () ved hjelp av sine egne attributter.
Vi har fått inn en grad av utvidbarhet ettersom former nå er en forekomst av formgrensesnitt. Dette lar oss bruke figur i stedet for individuelle klasser
Det siste punktet ovenfor nevnte forbruker av disse fasongene. I vårt tilfelle vil forbrukeren være i AreaCalculator-klassen som nå vil se slik ut.

offentlig klasse AreaCalculator {offentlig dobbel beregneShapeArea (Shape form) {return shape.calculateArea ()}}

Denne områdekalkulatorenklasse fjerner nå våre designfeil som er nevnt ovenfor og gir en ren løsning som følger det åpne-lukkede prinsippet. La oss gå videre med andre SOLID-prinsipper i Java

Liskov-substitusjonsprinsipp i Java

Robert C. Martin beskriver det som avledede typer må være helt substituerbare for basistypene.

Liskov-substitusjonsprinsippet forutsetter at q (x) er en egenskap som kan påvises om enheter av x som tilhører type T. Nå, ifølge dette prinsippet, bør q (y) nå kunne bevises for objekter y som tilhører type S, og S er faktisk en undertype av T. Er du nå forvirret og vet ikke hva Liskov-substitusjonsprinsippet egentlig betyr? Definisjonen av det kan være litt komplisert, men faktisk er det ganske enkelt. Det eneste er at hver underklasse eller avledet klasse skal kunne erstattes av foreldrene eller baseklassen.

Du kan si at det er et unikt objektorientert prinsipp. Prinsippet kan videre forenkles av en barnetype av en bestemt foreldretype uten å gjøre noen komplikasjoner eller å sprengte ting skal ha evnen til å stå inn for den forelderen. Dette prinsippet er nært knyttet til Liskov-substitusjonsprinsippet.

Hvorfor kreves dette prinsippet?

Dette unngår misbruk av arv. Det hjelper oss å tilpasse oss 'is-a' -forholdet. Vi kan også si at underklasser må oppfylle en kontrakt definert av basisklassen. Slik sett er det relatert tilDesign etter kontraktdet ble først beskrevet av Bertrand Meyer. For eksempel er det fristende å si at en sirkel er en type ellips, men sirkler har ikke to foci eller store / mindre akser.

LSP forklares populært ved hjelp av eksemplet med kvadrat og rektangel. hvis vi antar et ISA-forhold mellom kvadrat og rektangel. Dermed kaller vi 'Square er et rektangel.' Koden nedenfor representerer forholdet.

offentlig klasse Rektangel {privat int lengde privat int bredde offentlig int getLength () {retur lengde} offentlig ugyldig setLength (int lengde) {this.length = lengde} offentlig int getBreadth () {retur bredde} offentlig ugyldig setBreadth (int bredde) { this.breadth = bredde} public int getArea () {return this.length * this.breadth}}

Nedenfor er koden for Square. Vær oppmerksom på at Square utvider rektangel.

offentlig klasse Firkant utvider Rektangel {offentlig tomrom settBredde (int bredde) {super.setBredde (bredde) super.setLength (bredde)} offentlig tomrom setLength (int lengde) {super.setLength (lengde) super.setBreadth (lengde)}}

I dette tilfellet prøver vi å etablere et ISA-forhold mellom kvadrat og rektangel slik at det å kalle 'kvadrat er et rektangel' i koden nedenfor, vil begynne å oppføre seg uventet hvis en forekomst av kvadrat passeres. En påstandsfeil vil bli kastet i tilfelle det å sjekke for “Areal” og se etter “Breadth,” selv om programmet avsluttes når påstandsfeilen kastes på grunn av feil i områdekontrollen.

offentlig klasse LSPDemo {offentlig tomrom beregneArea (rektangel r) {r.setBreadth (2) r.setLength (3) hevde r.getArea () == 6: printError ('area', r) hevde r.getLength () == 3: printError ('lengde', r) hevder r.getBreadth () == 2: printError ('bredde', r)} privat streng printError (streng errorIdentifer, rektangel r) {return 'Uventet verdi av' + errorIdentifer + ' for eksempel av '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // En forekomst av rektangel sendes lsp.calculateArea (nytt rektangel ()) // En forekomst av firkant passeres lsp.calculateArea (ny firkant ())}}

Klassen demonstrerer Liskov Substitution Principle (LSP) I henhold til prinsippet må funksjonene som bruker referanser til basisklassene kunne bruke objekter av avledet klasse uten å vite det.

Således, i eksemplet vist nedenfor, skal funksjonen beregne område som bruker referansen til 'Rektangel' kunne bruke objektene i avledet klasse som Square og oppfylle kravet som stilles av Rectangle definition. Man bør merke seg at i henhold til definisjonen av rektangel, må følgende alltid være sant gitt dataene nedenfor:

  1. Lengde må alltid være lik lengden som inngår til metoden, setLength
  2. Bredden må alltid være lik bredden som sendes som input til metode, setBreadth
  3. Areal må alltid være lik produkt av lengde og bredde

I tilfelle vi prøver å etablere et ISA-forhold mellom kvadrat og rektangel slik at vi kaller 'kvadrat er et rektangel', vil ovennevnte kode begynne å oppføre seg uventet hvis en forekomst av kvadrat passeres. Påstandsfeil vil bli kastet i tilfelle kontroll av område og sjekk for bredden, selv om programmet avsluttes når påstandsfeilen kastes på grunn av feil i områdekontrollen.

Square-klassen trenger ikke metoder som setBreadth eller setLength. LSPDemo-klassen trenger å kjenne detaljene i avledede klasser av rektangel (for eksempel Square) for å kode riktig for å unngå kastfeil. Endringen i den eksisterende koden bryter i første omgang det åpne-lukkede prinsippet.

Grensesnitt Segregeringsprinsipp

Robert C. Martin beskriver det som at klienter ikke skal tvinges til å implementere unødvendige metoder som de ikke vil bruke.

hvordan trimme i java

I følgeGrensesnitt-segregeringsprinsippen klient, uansett hva som aldri skal tvinges til å implementere et grensesnitt som den ikke bruker, eller klienten skal aldri være forpliktet til å være avhengig av noen metode, som ikke brukes av dem. Så i utgangspunktet er grensesnittets segregeringsprinsipper slik du foretrekker grensesnitt, som er små, men klientspesifikke i stedet for monolitiske og større grensesnitt. Kort sagt, det ville være dårlig for deg å tvinge klienten til å stole på en bestemt ting, som de ikke trenger.

For eksempel er et enkelt logggrensesnitt for å skrive og lese logger nyttig for en database, men ikke for en konsoll. Å lese logger gir ingen mening for en konsollogger. Fortsetter med disse SOLID-prinsippene i Java-artikkelen.

Hvorfor kreves dette prinsippet?

La oss si at det er et restaurantgrensesnitt som inneholder metoder for å godta bestillinger fra online-kunder, inn- eller telefonkunder og walk-in-kunder. Den inneholder også metoder for å håndtere online betalinger (for online kunder) og personlige betalinger (for walk-in kunder så vel som telefonkunder når bestillingen blir levert hjemme).

La oss nå opprette et Java-grensesnitt for Restaurant og gi det navnet RestaurantInterface.java.

offentlig grensesnitt RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}

Det er fem metoder definert i RestaurantInterface som er å akseptere online bestilling, ta telefonbestilling, godta bestillinger fra en walk-in-kunde, godta online betaling og godta betaling personlig.

La oss starte med å implementere RestaurantInterface for online-kunder som OnlineClientImpl.java

offentlig klasse OnlineClientImpl implementerer RestaurantInterface {public void acceptOnlineOrder () {// logic for pleatsing online order} public void takeTelephoneOrder () {// Not Applicable for Online Order throw new UnsupportedOperationException ()} public void payOnline () {// logic for betalende online} public void walkInCustomerOrder () {// Not Applicable for Online Order throw new UnsupportedOperationException ()} public void payInPerson () {// Not Applicable for Online Order throw new UnsupportedOperationException ()}}
  • Siden koden ovenfor (OnlineClientImpl.java) er for online bestillinger, må du kaste UnsupportedOperationException.

  • Online, telefoniske og walk-in klienter bruker RestaurantInterface implementeringen som er spesifikk for hver av dem.

  • Implementeringsklasser for Telephonic-klient og Walk-in-klient har ikke-støttede metoder.

  • Siden de 5 metodene er en del av RestaurantInterface, må implementeringsklassene implementere alle 5 av dem.

  • Metodene som hver av implementeringsklassene kaster UnsupportedOperationException. Som du tydelig kan se - implementering av alle metoder er ineffektiv.

  • Enhver endring i noen av metodene til RestaurantInterface blir spredt til alle implementeringsklasser. Vedlikehold av kode begynner å bli veldig tungvint, og regresjonseffekter av endringer vil fortsette å øke.

  • RestaurantInterface.java bryter enkeltansvarsprinsippet fordi logikken for betalinger så vel som for ordreplassering er samlet i et enkelt grensesnitt.

For å overvinne de ovennevnte problemene, bruker vi grensesnitt-segregeringsprinsipp for å omformulere ovennevnte design.

  1. Separer ut betalings- og ordreplasseringsfunksjoner i to separate magre grensesnitt, PaymentInterface.java og OrderInterface.java.

  2. Hver av klientene bruker én implementering hver av PaymentInterface og OrderInterface. For eksempel - OnlineClient.java bruker OnlinePaymentImpl og OnlineOrderImpl og så videre.

  3. Prinsippet om enkelt ansvar er nå lagt til som betalingsgrensesnitt (PaymentInterface.java) og Ordering-grensesnitt (OrderInterface).

  4. Endring i noen av bestillings- eller betalingsgrensesnittene påvirker ikke den andre. De er uavhengige nå. Det vil ikke være behov for å utføre noen dummyimplementering eller kaste et ikke-støttet driftseksept, ettersom hvert grensesnitt bare har metoder det alltid vil bruke.

Etter påføring av ISP

Avhengighetsinversjonsprinsipp

Robert C. Martin beskriver det som det avhenger av abstraksjoner og ikke av konkresjoner. I følge det må høynivåmodulen aldri stole på noen lavnivåmodul. for eksempel

Du går til en lokal butikk for å kjøpe noe, og du bestemmer deg for å betale for det ved hjelp av debetkortet ditt. Så når du gir kortet ditt til ekspeditøren for å utføre betalingen, gidder ekspeditøren ikke å sjekke hva slags kort du har gitt.

Selv om du har gitt et Visa-kort, vil han ikke sette ut en Visa-maskin for å sveipe kortet ditt. Hvilken type kredittkort eller debetkort du har for å betale, spiller ikke en gang noen rolle, de vil bare sveipe det. Så i dette eksemplet kan du se at både du og ekspeditøren er avhengige av kredittkortabstraksjonen, og at du ikke er bekymret for kortets detaljer. Dette er hva et avhengighetsinversjonsprinsipp er.

Hvorfor kreves dette prinsippet?

Det lar en programmerer fjerne hardkodede avhengigheter, slik at applikasjonen blir løst koblet og utvidbar.

offentlig klasse Student {privat adresse adresse offentlig student () {adresse = ny adresse ()}}

I eksemplet ovenfor krever studentklassen et adresseobjekt, og den er ansvarlig for å initialisere og bruke adresseobjektet. Hvis adresseklassen endres i fremtiden, må vi også gjøre endringer i studentklassen. Dette gjør den tette koblingen mellom student- og adresseobjekter. Vi kan løse dette problemet ved hjelp av designmønsteret for avhengighetsinversjon. dvs. adresseobjekt vil bli implementert uavhengig og vil bli gitt til student når student blir instantiert ved hjelp av konstruktørbasert eller setterbasert avhengighetsinversjon.

Med dette kommer vi til en slutt på disse SOLID-prinsippene i Java.

Sjekk ut av Edureka, et pålitelig online læringsfirma med et nettverk av mer enn 250 000 fornøyde elever spredt over hele verden. Edurekas Java J2EE- og SOA-opplæring og sertifiseringskurs er designet for studenter og fagpersoner som ønsker å være Java-utvikler. Kurset er designet for å gi deg et forsprang i Java-programmering og trene deg for både kjerne- og avanserte Java-konsepter sammen med forskjellige Java-rammer som Hibernate & Spring.

Har du et spørsmål til oss? Vennligst nevn det i kommentarfeltet i denne “SOLID Principles in Java” -bloggen, så kommer vi tilbake til deg så snart som mulig.