Alt hvad du behøver at vide om solide principper i Java



I denne artikel lærer du i detaljer om, hvad der er solide principper i java med eksempler og deres betydning med eksempler fra det virkelige liv.

I verden af (OOP), der er mange designretningslinjer, mønstre eller principper. Fem af disse principper er normalt grupperet sammen og er kendt under akronymet SOLID. Mens hvert af disse fem principper beskriver noget specifikt, overlapper de også, således at vedtagelse af et af dem indebærer eller fører til vedtagelse af et andet. I denne artikel vil vi forstå SOLID-principper i Java.

Historie af SOLID-principper i Java

Robert C. Martin gav fem objektorienterede designprincipper, og akronymet 'S.O.L.I.D' bruges til det. Når du bruger alle S.O.L.I.D's principper på en kombineret måde, bliver det lettere for dig at udvikle software, der let kan administreres. De andre funktioner ved brug af S.O.L.I.D er:





  • Det undgår kodelugt.
  • Refraktorkode hurtigt.
  • Kan udføre adaptiv eller smidig softwareudvikling.

Når du bruger princippet om S.O.L.I.D i din kodning, begynder du at skrive den kode, der er både effektiv og effektiv.



Hvad er betydningen af ​​S.O.L.I.D?

Fast repræsenterer fem principper for java, som er:

  • S : Enkeltansvarsprincip
  • ELLER : Åben-lukket princip
  • L : Liskov-substitutionsprincip
  • jeg : Interface-adskillelsesprincip
  • D : Afhængighedsinversionsprincip

I denne blog vil vi diskutere alle de fem SOLID-principper for Java i detaljer.



Princip for et enkelt ansvar i Java

Hvad står der?

Robert C. Martin beskriver det, som at en klasse kun skulle have et eneste ansvar.

Ifølge princippet om et enkelt ansvar skal der kun være en grund, som en klasse skal ændres for. Det betyder, at en klasse skal have en opgave at gøre. Dette princip betegnes ofte som subjektivt.

Princippet kan forstås godt med et eksempel. Forestil dig, at der er en klasse, der udfører følgende operationer.

  • Forbundet til en database

  • Læs nogle data fra databasetabeller

  • Til sidst skal du skrive det til en fil.

Har du forestillet dig scenariet? Her har klassen flere grunde til at ændre, og få af dem er ændring af filoutput, vedtagelse af ny database. Når vi taler om et enkelt principansvar, vil vi sige, at der er for mange grunde til, at klassen kan ændre sig, derfor passer den ikke ordentligt i det enkelte ansvarsprincip.

For eksempel kan en Automobile-klasse starte eller stoppe sig selv, men opgaven med at vaske den hører til CarWash-klassen. I et andet eksempel har en bogklasse egenskaber til at gemme sit eget navn og sin egen tekst. Men opgaven med at udskrive bogen skal tilhøre klassen Bogprinter. Klassen Bogprinter udskriver muligvis til konsol eller et andet medium, men sådanne afhængigheder fjernes fra bogklassen

Hvorfor kræves dette princip?

Når princippet om et enkelt ansvar følges, er test nemmere. Med et enkelt ansvar har klassen færre testsager. Mindre funktionalitet betyder også færre afhængigheder af andre klasser. Det fører til bedre kodeorganisation, da mindre og velindrettede klasser er lettere at søge på.

Et eksempel på at præcisere dette princip:

Antag, at du bliver bedt om at implementere en UserSetting-tjeneste, hvor brugeren kan ændre indstillingerne, men før det skal brugeren godkendes. En måde at 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) {// Kontroller, om brugeren er gyldig. }}

Alt ser godt ud, indtil du vil genbruge checkAccess-koden et andet sted ELLER du vil foretage ændringer i den måde, checkAccess udføres på. I alle to tilfælde vil du ende med at ændre den samme klasse, og i det første tilfælde skal du bruge UserSettingService til at kontrollere også om adgang.
En måde at rette op på dette er at nedbryde UserSettingService til UserSettingService og SecurityService. Og flyt 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. }}

Åbn lukket princip i Java

Robert C. Martin beskriver det som softwarekomponenter, der skal være åbne for udvidelse, men lukkede for ændringer.

lære pl sql online gratis

For at være præcis, ifølge dette princip skal en klasse skrives på en sådan måde, at den udfører sit job fejlfrit uden den antagelse, at folk i fremtiden simpelthen vil komme og ændre det. Derfor skal klassen forblive lukket for ændringer, men den skal have mulighed for at blive udvidet. Måder at udvide klassen inkluderer:

  • Arve fra klassen

  • Overskrivning af den krævede adfærd fra klassen

  • Udvidelse af visse adfærd i klassen

Et glimrende eksempel på åben-lukket princip kan forstås ved hjælp af browsere. Kan du huske, at du har installeret udvidelser i din Chrome-browser?

Grundlæggende funktion i Chrome-browseren er at surfe på forskellige sider. Vil du kontrollere grammatik, når du skriver en e-mail ved hjælp af chrome browser? Hvis ja, kan du bare bruge Grammarly-udvidelse, det giver dig grammatikkontrol af indholdet.

Denne mekanisme, hvor du tilføjer ting til at øge browserens funktionalitet, er en udvidelse. Derfor er browseren et perfekt eksempel på funktionalitet, der er åben for udvidelse, men er lukket for ændring. Med enkle ord kan du forbedre funktionaliteten ved at tilføje / installere plugins i din browser, men kan ikke opbygge noget nyt.

Hvorfor kræves dette princip?

OCP er vigtig, da klasser kan komme til os gennem tredjepartsbiblioteker. Vi skulle være i stand til at udvide disse klasser uden at bekymre os, hvis disse basisklasser kan understøtte vores udvidelser. Men arv kan føre til underklasser, der afhænger af baseklassens implementering. For at undgå dette anbefales brug af grænseflader. Denne ekstra abstraktion fører til løs kobling.

Lad os sige, at vi skal beregne områder i forskellige former. Vi starter med at oprette en klasse til vores første form rektangelsom har to attributter længde& bredde.

offentlig klasse Rektangel {offentlig dobbelt længde offentlig dobbelt bredde}

Derefter opretter vi en klasse til at beregne arealet af dette rektangelsom har en metode til at beregneRectangleAreasom tager rektangletsom inputparameter og beregner dets areal.

offentlig klasse AreaCalculator {offentlig dobbelt beregneRektangelArea (rektangel-rektangel) {returner rektangel.længde * rektangel.bredde}}

Så langt så godt. Lad os sige, at vi får vores anden form cirkel. Så vi opretter straks en ny klassecirkelmed en enkelt attributradius.

offentlig klasse Cirkel {offentlig dobbeltradius}

Derefter ændrer vi Areacalculatorklasse for at tilføje cirkelberegninger gennem en ny metode, beregneCircleaArea ()

offentlig klasse AreaCalculator {offentlig dobbelt beregneRektangelArea (Rektangel rektangel) {retur rektangel.længde * rektangel.bredde} offentlig dobbelt beregneCircleArea (Cirkel cirkel) {retur (22/7) * cirkel.radius * cirkel.radius}}

Bemærk dog, at der var fejl i den måde, vi designet vores løsning ovenfor på.

Lad os sige, at vi har en ny form femkant. I så fald ender vi med at ændre klassen AreaCalculator. Efterhånden som formerne vokser, bliver dette mere rodet, da AreaCalculator bliver ved med at ændre sig, og alle forbrugere i denne klasse bliver nødt til at fortsætte med at opdatere deres biblioteker, der indeholder AreaCalculator. Som et resultat vil AreaCalculator-klassen ikke være baselineret (afsluttet) med sikkerhed, da hver gang en ny form kommer, vil den blive ændret. Så dette design er ikke lukket for ændringer.

AreaCalculator bliver nødt til at fortsætte med at tilføje deres beregningslogik i nyere metoder. Vi udvider ikke rigtig omfanget af former, men vi laver simpelthen stykke-måltid-løsning (bit-for-bit) for hver form, der tilføjes.

Ændring af ovenstående design for at overholde åbnet / lukket princip:

Lad os nu se et mere elegant design, der løser fejlene i ovenstående design ved at overholde det åbne / lukkede princip. Vi vil først og fremmest gøre designet udvideligt. Til dette skal vi først definere en basistype Shape og have Circle & Rectangle implement Shape interface.

offentlig grænseflade Form {offentlig dobbelt beregneArea ()} offentlig klasse Rektangel redskaber Form {dobbelt længde dobbelt bredde offentlig dobbelt beregneArea () {retur længde * bredde}} offentlig klasse Cirkel redskaber figur {offentlig dobbelt radius offentlig dobbelt beregne område () {retur (22 / 7) * radius * radius}}

Der er en base-interface-form. Alle former implementerer nu basisgrænsefladen Shape. Formgrænsefladen har en abstrakt metode til beregning af området (). Både cirkel og rektangel giver deres egen tilsidesatte implementering af beregningsarea () -metoden ved hjælp af deres egne attributter.
Vi har bragt en grad af udvidelighed, da figurer nu er en forekomst af formgrænseflader. Dette giver os mulighed for at bruge form i stedet for individuelle klasser
Det sidste punkt ovenfor nævnte forbruger af disse former. I vores tilfælde vil forbrugeren være i AreaCalculator-klassen, som nu vil se sådan ud.

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

Denne AreaCalculatorklasse fjerner nu vores designfejl, der er nævnt ovenfor, og giver en ren løsning, der overholder det åben-lukkede princip. Lad os gå videre med andre SOLID-principper i Java

Liskov-substitutionsprincip i Java

Robert C. Martin beskriver det som afledte typer skal være helt substituerbare med deres basetyper.

Liskov-substitutionsprincippet antager, at q (x) er en egenskab, der kan påvises omkring enheder af x, der tilhører type T. Nu skal q (y) ifølge dette princip nu kunne bevises for objekter y, der tilhører type S, og S er faktisk en undertype af T. Er du nu forvirret og ved ikke, hvad Liskov-substitutionsprincippet egentlig betyder? Definitionen af ​​det kan være lidt kompleks, men faktisk er det ret let. Det eneste er, at hver underklasse eller afledt klasse skal kunne erstattes af deres forælder eller basisklasse.

Du kan sige, at det er et unikt objektorienteret princip. Princippet kan yderligere forenkles med en barnetype af en bestemt forældretype uden at gøre nogen komplikationer, eller at sprænge ting op skal have evnen til at stå ind for den forælder. Dette princip er tæt knyttet til Liskov-substitutionsprincippet.

Hvorfor kræves dette princip?

Dette undgår misbrug af arv. Det hjælper os med at tilpasse os 'is-a' -forholdet. Vi kan også sige, at underklasser skal opfylde en kontrakt defineret af basisklassen. I denne forstand er det relateret tilDesign efter kontraktdet blev først beskrevet af Bertrand Meyer. For eksempel er det fristende at sige, at en cirkel er en type ellipse, men cirkler har ikke to foci eller større / mindre akser.

LSP forklares populært ved hjælp af eksemplet med kvadrat og rektangel. hvis vi antager et ISA-forhold mellem firkant og rektangel. Således kalder vi 'Firkant er et rektangel.' Koden nedenfor repræsenterer forholdet.

offentlig klasse Rektangel {privat int længde privat int bredde offentlig int getLength () {retur længde} offentlig tomrum sætLængde (int længde) {denne.længde = længde} offentlig int getBreadth () {retur bredde} offentlig tom sætBredde (int bredde) { this.breadth = bredde} public int getArea () {returner this.length * this.breadth}}

Nedenfor er koden til Square. Bemærk, at firkantet udvider rektangel.

php print_r array
offentlig klasse Firkant udvider rektangel {public void setBreadth (int width) {super.setBreadth (width) super.setLength (width)} public void setLength (int length) {super.setLength (length) super.setBreadth (længde)}}

I dette tilfælde forsøger vi at etablere et ISA-forhold mellem firkant og rektangel, således at kalde 'firkant er et rektangel' i nedenstående kode vil begynde at opføre sig uventet, hvis en forekomst af firkant overføres. En påstandsfejl vil blive kastet i tilfælde af kontrol af 'Område' og kontrol af 'Bredde', selvom programmet afsluttes, når påstandsfejlen kastes på grund af fejl i områdekontrollen.

offentlig klasse LSPDemo {offentlig tomrum beregneArea (rektangel r) {r.setBreadth (2) r.setLength (3) hævde r.getArea () == 6: printError ('område', r) hævde r.getLength () == 3: printError ('længde', r) hævder r.getBreadth () == 2: printError ('bredde', r)} privat streng printError (strengfejlIdentifer, rektangel r) {return 'Uventet værdi af' + errorIdentifer + ' for eksempel af '+ r.getClass (). getName ()} offentlig statisk ugyldig hoved (String [] args) {LSPDemo lsp = new LSPDemo () // En forekomst af rektangel videregives lsp.calculateArea (nyt rektangel ()) // En forekomst af firkant passeres lsp.calculateArea (ny firkant ())}}

Klassen demonstrerer Liskov-substitutionsprincippet (LSP) I henhold til princippet skal de funktioner, der bruger referencer til basisklasserne, kunne bruge objekter af afledt klasse uden at vide det.

I eksemplet vist nedenfor skal funktionen calcalArea, der bruger referencen til 'Rektangel', være i stand til at bruge objekterne fra den afledte klasse som Firkant og opfylde kravet, der stilles af Rektangel-definitionen. Man skal bemærke, at i henhold til definitionen af ​​rektangel skal følgende altid være sandt i betragtning af nedenstående data:

  1. Længde skal altid være lig med den længde, der er gået som input til metoden, setLength
  2. Bredde skal altid være lig med bredden, der sendes som input til metode, setBreadth
  3. Areal skal altid være lig med produkt af længde og bredde

Hvis vi prøver at etablere ISA-forhold mellem firkant og rektangel, således at vi kalder 'firkant er et rektangel', vil ovenstående kode begynde at opføre sig uventet, hvis en forekomst af firkant er bestået. Påstandsfejl vil blive kastet i tilfælde af kontrol af område og kontrol for bredden, selvom programmet afsluttes, når påstandsfejlen smides på grund af fejl i områdekontrollen.

Square-klassen har ikke brug for metoder som setBreadth eller setLength. LSPDemo-klassen har brug for at kende detaljerne i afledte klasser af rektangel (såsom firkant) for at kode korrekt for at undgå kastfejl. Ændringen i den eksisterende kode bryder i første omgang det åbne-lukkede princip.

Princip for adskillelse af grænseflade

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

IfølgePrincip for adskillelse af grænsefladeen klient, uanset hvad der aldrig skulle tvinges til at implementere en grænseflade, som den ikke bruger, eller klienten bør aldrig være forpligtet til at være afhængig af nogen metode, som ikke bruges af dem. Så grundlæggende er grænsefladeseparationsprincipperne, som du foretrækker grænseflader, som er små, men klientspecifikke i stedet for monolitiske og større grænseflader. Kort sagt ville det være dårligt for dig at tvinge klienten til at stole på en bestemt ting, som de ikke har brug for.

For eksempel er en enkelt logningsgrænseflade til skrivning og læsning af logfiler nyttig til en database, men ikke til en konsol. Læsning af logfiler giver ingen mening for en konsollogger. Fortsætter med disse SOLID-principper i Java-artiklen.

Hvorfor kræves dette princip?

Lad os sige, at der er en restaurantgrænseflade, der indeholder metoder til at acceptere ordrer fra online-kunder, opkalds- eller telefonkunder og walk-in-kunder. Den indeholder også metoder til håndtering af onlinebetalinger (for online-kunder) og personlige betalinger (for walk-in-kunder såvel som telefonkunder, når deres ordre leveres hjemme).

Lad os nu oprette et Java-interface til restaurant og navngive det som RestaurantInterface.java.

offentlig grænseflade RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}

Der er defineret 5 metoder i RestaurantInterface, som er til at acceptere onlineordre, tage telefonisk ordre, acceptere ordrer fra en walk-in-kunde, acceptere online betaling og acceptere betaling personligt.

Lad os starte med at 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 () {// logik for betaling online} offentlig ugyldig walkInCustomerOrder () {// Ikke relevant for online ordre kast ny UnsupportedOperationException ()} offentlig ugyldig payInPerson () {// Ikke relevant for online ordre kast ny UnsupportedOperationException ()}}
  • Da ovenstående kode (OnlineClientImpl.java) er til online ordrer, skal du kaste UnsupportedOperationException.

  • Online-, telefon- og walk-in-klienter bruger RestaurantInterface-implementeringen specifikt for hver af dem.

  • Implementeringsklasser for telefonisk klient og walk-in-klient har ikke-understøttede metoder.

  • Da de 5 metoder er en del af RestaurantInterface, skal implementeringsklasserne implementere dem alle 5.

  • Metoderne, som hver af implementeringsklasserne kaster UnsupportedOperationException. Som du tydeligt kan se - implementering af alle metoder er ineffektiv.

  • Enhver ændring i en hvilken som helst af metoderne i RestaurantInterface vil blive formidlet til alle implementeringsklasser. Vedligeholdelse af kode begynder derefter at blive virkelig besværlig, og regressionseffekter af ændringer vil fortsætte med at stige.

  • RestaurantInterface.java bryder enkeltansvarsprincippet, fordi logikken for betalinger såvel som for ordreafgivelse er samlet i en enkelt grænseflade.

For at overvinde de ovennævnte problemer anvender vi grænsefladeseparationsprincip for at omlægge ovenstående design.

  1. Adskil udbetalings- og ordreplaceringsfunktioner i to separate leangrænseflader, PaymentInterface.java og OrderInterface.java.

  2. Hver af klienterne bruger en implementering hver af PaymentInterface og OrderInterface. For eksempel - OnlineClient.java bruger OnlinePaymentImpl og OnlineOrderImpl og så videre.

  3. Princippet om et enkelt ansvar er nu knyttet som betalingsgrænseflade (PaymentInterface.java) og Ordering-interface (OrderInterface).

  4. Ændring i en af ​​ordre- eller betalingsgrænsefladerne påvirker ikke den anden. De er uafhængige nu. Der vil ikke være behov for at foretage nogen dummy-implementering eller kaste en ikke-understøttet driftsundtagelse, da hver grænseflade kun har metoder, den altid vil bruge.

Efter anvendelse af internetudbyder

Princip for afhængighedsinversion

Robert C. Martin beskriver det som det afhænger af abstraktioner og ikke af konkretioner. Ifølge det må højniveaumodulet aldrig stole på noget lavt niveau modul. for eksempel

Du går til en lokal butik for at købe noget, og du beslutter at betale for det ved hjælp af dit betalingskort. Så når du giver dit kort til ekspeditøren for at foretage betalingen, gider ekspeditøren ikke at kontrollere, hvilken slags kort du har givet.

Selv hvis du har givet et Visa-kort, lægger han ikke en Visa-maskine til at stryge dit kort. Den type kreditkort eller betalingskort, du har til at betale, betyder ikke engang, de vil bare skubbe det. Så i dette eksempel kan du se, at både dig og ekspeditøren er afhængige af kreditkortabstraktionen, og du er ikke bekymret for kortets detaljer. Dette er, hvad et afhængighedsinversionsprincip er.

Hvorfor kræves dette princip?

Det giver en programmør mulighed for at fjerne hardkodede afhængigheder, så applikationen bliver løst koblet og udvidelig.

offentlig klasse elev {privat adresse adresse offentlig studerende () {adresse = ny adresse ()}}

I ovenstående eksempel kræver elevklassen et adresseobjekt, og det er ansvarligt for initialisering og brug af adresseobjektet. Hvis adresseklassen ændres i fremtiden, skal vi også foretage ændringer i elevklassen. Dette gør den tætte kobling mellem elev- og adresseobjekter. Vi kan løse dette problem ved hjælp af designmønsteret for afhængighedsinversion. dvs. adresseobjekt vil blive implementeret uafhængigt og vil blive leveret til studerende, når student instantieres ved hjælp af konstruktørbaseret eller setterbaseret afhængighedsinversion.

Med dette kommer vi til en ende af disse SOLID-principper i Java.

Tjek den af Edureka, et pålideligt online læringsfirma med et netværk på mere end 250.000 tilfredse elever spredt over hele kloden. Edurekas Java J2EE- og SOA-uddannelses- og certificeringskursus er designet til studerende og fagfolk, der ønsker at være Java-udvikler. Kurset er designet til at give dig et forspring i Java-programmering og træne dig til både kerne- og avancerede Java-koncepter sammen med forskellige Java-rammer som Hibernate & Spring.

Har du et spørgsmål til os? Nævn det i kommentarfeltet i denne “SOLID Principles in Java” -blog, og vi vender tilbage til dig hurtigst muligt.

hvordan man bruger splitfunktion i python