Všetko, čo potrebujete vedieť o pevných princípoch v Jave



V tomto článku sa podrobne dozviete, čo sú pevné princípy v jave, s príkladmi a ich dôležitosťou s príkladom z reálneho života.

Vo svete (OOP), existuje veľa návrhových pokynov, vzorov alebo zásad. Päť z týchto princípov je zvyčajne zoskupených a sú známe pod skratkou SOLID. Aj keď každý z týchto piatich princípov popisuje niečo konkrétne, navzájom sa prekrývajú, takže prijatie jedného z nich znamená alebo vedie k prijatiu druhého. V tomto článku porozumieme princípom SOLID v Jave.

História princípov SOLID v Jave

Robert C. Martin uviedol päť objektovo orientovaných princípov návrhu a používa sa pre neho skratka „S.O.L.I.D“. Keď využijete všetky princípy S.O.L.I.D kombinovaným spôsobom, bude pre vás jednoduchšie vyvíjať softvér, ktorý sa dá ľahko spravovať. Ďalšie funkcie používania S.O.L.I.D sú:





  • Vyhýba sa pachom kódu.
  • Rýchlo kód refraktora.
  • Dokáže adaptívny alebo svižný vývoj softvéru.

Keď pri kódovaní použijete princíp S.O.L.I.D, začnete písať efektívny a efektívny kód.



Aký význam má S.O.L.I.D?

Solid predstavuje päť princípov javy, ktoré sú:

  • S : Zásada jedinej zodpovednosti
  • ALEBO : Princíp otvorenia a zatvorenia
  • Ľ : Princíp substitúcie Liskov
  • Ja : Princíp segregácie rozhrania
  • D : Princíp inverzie závislostí

V tomto blogu sa budeme podrobne venovať všetkým piatim SOLID princípom Javy.



Princíp jedinej zodpovednosti v Jave

Čo to hovorí?

Robert C. Martin to popisuje tak, že jedna trieda by mala mať iba jednu a jedinú zodpovednosť.

Podľa zásady jedinej zodpovednosti by mal existovať iba jeden dôvod, kvôli ktorému musí byť trieda zmenená. To znamená, že trieda by mala mať jednu úlohu. Tento princíp sa často nazýva subjektívny.

Fibonacciho séria c ++

Princíp možno dobre pochopiť na príklade. Predstavte si, že existuje trieda, ktorá vykonáva nasledujúce operácie.

  • Pripojené k databáze

  • Prečítajte si niektoré údaje z databázových tabuliek

  • Nakoniec to napíšte do súboru.

Predstavili ste si scenár? Trieda tu má niekoľko dôvodov na zmenu a málo z nich je modifikácia výstupu súboru, prijatie novej databázy. Keď hovoríme o zodpovednosti za jednu zásadu, povedali by sme, že existuje príliš veľa dôvodov na to, aby sa trieda zmenila, preto to do princípu jednej zodpovednosti nesedí správne.

Napríklad trieda Automobil sa môže sama spustiť alebo zastaviť, ale jej umývanie patrí do triedy CarWash. V inom príklade má trieda Kniha vlastnosti na ukladanie vlastného mena a textu. Úloha vytlačiť knihu však musí patriť do triedy kníhtlačiarní. Trieda kníhtlačiarní môže tlačiť na konzolu alebo iné médium, ale takéto závislosti sú z triedy kníh odstránené

Prečo je tento princíp požadovaný?

Ak sa bude dodržiavať zásada jedinej zodpovednosti, testovanie je jednoduchšie. S jedinou zodpovednosťou bude mať trieda menej testovacích prípadov. Menej funkčnosti znamená aj menej závislostí od iných tried. Vedie to k lepšej organizácii kódu, pretože menšie a dobre zostavené triedy sa dajú ľahšie vyhľadávať.

Príklad na objasnenie tejto zásady:

Predpokladajme, že sa zobrazí výzva na implementáciu služby UserSetting, v ktorej môže používateľ meniť nastavenia, predtým však musí byť autentifikovaný. Jedným zo spôsobov, ako to implementovať, by bol:

public class UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// Grant option to change}} public boolean checkAccess (User user) {// Overte, či je používateľ platný. }}

Všetko vyzerá dobre, kým nebudete chcieť znova použiť kód checkAccess na inom mieste ALEBO nebudete chcieť zmeniť spôsob, akým sa vykonáva checkAccess. Vo všetkých 2 prípadoch by ste nakoniec zmenili rovnakú triedu a v prvom prípade by ste na kontrolu prístupu museli tiež použiť UserSettingService.
Jedným zo spôsobov, ako to napraviť, je rozklad UserSettingService na UserSettingService a SecurityService. A presuňte kód checkAccess do 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. }}

Otvorte uzavretý princíp v Jave

Robert C. Martin to popisuje ako softvérové ​​komponenty by mali byť otvorené pre rozšírenie, ale uzavreté pre modifikáciu.

Presne povedané, podľa tohto princípu by trieda mala byť napísaná tak, aby svoju prácu vykonávala bezchybne bez predpokladu, že ľudia v budúcnosti jednoducho prídu a zmenia ju. Trieda by preto mala zostať uzavretá kvôli úpravám, mala by však mať možnosť rozšíriť sa. Medzi spôsoby rozšírenia triedy patria:

  • Dedenie z triedy

  • Prepísať požadované správanie z triedy

  • Rozšírenie určitého správania sa triedy

Vynikajúci príklad princípu otvoreného a zatvoreného je možné pochopiť pomocou prehľadávačov. Pamätáte si, ako ste si nainštalovali rozšírenia do svojho prehliadača Chrome?

Základnou funkciou prehliadača Chrome je surfovanie na rôznych stránkach. Chcete skontrolovať gramatiku pri písaní e-mailu pomocou prehliadača Chrome? Ak áno, môžete jednoducho použiť rozšírenie Grammarly, ktoré vám umožní skontrolovať gramatiku obsahu.

Tento mechanizmus, do ktorého pridávate veci na zvýšenie funkčnosti prehľadávača, je rozšírením. Prehliadač je teda dokonalým príkladom funkčnosti, ktorá je otvorená pre rozšírenie, ale je uzavretá pre úpravy. Jednoducho povedané, môžete vylepšiť funkčnosť pridaním / inštaláciou doplnkov do prehliadača, nemôžete však vytvárať nič nové.

Prečo je táto zásada požadovaná?

OCP je dôležitý, pretože triedy k nám môžu prichádzať prostredníctvom knižníc tretích strán. Mali by sme byť schopní rozšíriť tieto triedy bez obáv, že tieto základné triedy môžu naše rozšírenia podporovať. Ale dedičnosť môže viesť k podtriedam, ktoré závisia od implementácie základnej triedy. Aby sa tomu zabránilo, odporúča sa používať rozhrania. Táto dodatočná abstrakcia vedie k voľnému spojeniu.

Povedzme, že musíme vypočítať oblasti rôznych tvarov. Začneme vytvorením triedy pre náš prvý tvar Obdĺžnikktorý má 2 atribúty dĺžka& šírka.

verejná trieda Obdĺžnik {verejná dvojitá dĺžka verejná dvojitá šírka}

Ďalej vytvoríme triedu na výpočet plochy tohto obdĺžnikaktorý má metódu CalcRectangleAreaktorý zaberá obdĺžnikako vstupný parameter a vypočíta jeho plochu.

verejná trieda AreaCalculator {verejné dvojité počítanieRectangleArea (obdĺžnikový obdĺžnik) {návratový obdĺžnik.dĺžka * obdĺžnik. šírka}}

Zatiaľ je všetko dobré. Teraz povedzme, že dostaneme náš druhý tvarový kruh. Okamžite teda vytvoríme novú triedu Circles jediným polomerom atribútu.

verejná trieda Circle {public double radius}

Potom upravíme Areacalculatortriedy na pridanie kruhových výpočtov pomocou novej metódy countCircleaArea ()

verejná trieda AreaCalculator {verejné dvojité vypočítaťRectangleArea (obdĺžnikový obdĺžnik) {návratový obdĺžnik.dĺžka * obdĺžnik. šírka} verejné dvojité vypočítaťCircleArea (kruh kruhu) {návrat (22/7) * kruh.radius * kruh.radius}}

Upozorňujeme však, že v spôsobe riešenia nášho riešenia vyššie boli nedostatky.

Povedzme, že máme nový tvar päťuholníka. V takom prípade opäť skončíme s úpravou triedy AreaCalculator. S pribúdajúcimi typmi tvarov sa to stáva chaotickejším, pretože AreaCalculator sa neustále mení a všetci spotrebitelia tejto triedy budú musieť neustále aktualizovať svoje knižnice, ktoré obsahujú AreaCalculator. Výsledkom bude, že trieda AreaCalculator nebude baseline (finalizovaná) so zárukou, pretože zakaždým, keď príde nový tvar, bude upravený. Tento dizajn teda nie je uzavretý kvôli zmene.

AreaCalculator bude musieť naďalej pridávať svoju výpočtovú logiku v novších metódach. V skutočnosti nerozširujeme rozsah tvarov, skôr robíme riešenie kúsok po kúsku pre každý pridaný tvar.

Úprava vyššie uvedeného dizajnu tak, aby vyhovovala princípu otvorenia / zatvorenia:

Pozrime sa teraz na elegantnejší dizajn, ktorý rieši chyby vyššie uvedeného dizajnu dodržiavaním princípu otvorenia / zatvorenia. Najskôr urobíme dizajn rozšíriteľným. Za týmto účelom musíme najskôr definovať základný typ Tvar a nechať implementovať rozhranie Tvar Circle & Rectangle.

verejné rozhranie Tvar {verejné dvojité vypočítaťArea ()} verejná trieda Obdĺžnikové nástroje implementovať Tvar {dvojitá dĺžka dvojitá šírka verejné dvojité vypočítaťArea () {návratová dĺžka * šírka}} verejná trieda Circle implementuje tvar {verejné dvojitý polomer verejné dvojité vypočítaťArea () {návrat (22 / 7) * polomer * polomer}}

K dispozícii je základné rozhranie Tvar. Všetky tvary teraz implementujú základné rozhranie Tvar. Tvarové rozhranie má abstraktnú metódu CalculateArea (). Kruh aj obdĺžnik poskytujú vlastnú prepísanú implementáciu metódy CalcArea () pomocou svojich vlastných atribútov.
Priniesli sme stupeň rozšíriteľnosti, pretože tvary sú teraz inštanciou tvarových rozhraní. To nám umožňuje používať tvar namiesto jednotlivých tried
Posledný bod vyššie spomínal spotrebiteľa týchto tvarov. V našom prípade bude spotrebiteľom trieda AreaCalculator, ktorá by teraz vyzerala takto.

verejná trieda AreaCalculator {verejné zdvojnásobenie CalcShapeArea (tvarový tvar) {návratový tvar.calculateArea ()}}

Tento AreaCalculatortrieda teraz úplne odstraňuje naše vyššie uvedené chyby v dizajne a poskytuje čisté riešenie, ktoré dodržiava princíp otvoreného a zatvoreného. Poďme k ďalším princípom SOLID v Jave

Princíp substitúcie Liskov v Jave

Robert C. Martin to opisuje ako Odvodené typy musia byť úplne nahraditeľné svojimi základnými typmi.

Princíp substitúcie Liskov predpokladá, že q (x) je vlastnosť dokázateľná o entitách x, ktorá patrí k typu T. Teraz by podľa tohto princípu mala byť q (y) dokázateľná pre objekty y patriace k typu S a S je vlastne podtypom T. Ste teraz zmätení a neviete, čo vlastne znamená princíp substitúcie Liskov? Jeho definícia môže byť trochu zložitá, ale v skutočnosti je to celkom ľahké. Jediná vec je, že každá podtrieda alebo odvodená trieda by mala byť nahraditeľná svojou materskou alebo základnou triedou.

Môžete povedať, že ide o jedinečný objektovo orientovaný princíp. Princíp môže byť ďalej zjednodušený detským typom konkrétneho typu rodiča bez toho, aby spôsoboval komplikácie alebo aby vyhodenie do povetria malo mať schopnosť zastupovať tohto rodiča. Tento princíp úzko súvisí s princípom substitúcie Liskov.

Prečo je táto zásada požadovaná?

Vyhnete sa tak zneužitiu dedičstva. Pomáha nám prispôsobiť sa vzťahu „je-a“. Môžeme tiež povedať, že podtriedy musia plniť zmluvu definovanú základnou triedou. V tomto zmysle to súvisí sNávrh podľa zmluvyto prvýkrát opísal Bertrand Meyer. Napríklad je lákavé povedať, že kruh je typ elipsy, ale kruhy nemajú dve ohniská alebo hlavné / vedľajšie osi.

LSP sa populárne vysvetľuje na príklade štvorcov a obdĺžnikov. ak predpokladáme vzťah ISA medzi štvorcom a obdĺžnikom. Hovoríme teda „štvorec je obdĺžnik“. Nasledujúci kód predstavuje vzťah.

public class Rectangle {private int length private int widthth public int getLength () {return length} public void setLength (int length) {this.length = length} public int getBreadth () {return widthth} public void setBreadth (int widthth) { this.breadth = šírka} public int getArea () {return this.length * this.breadth}}

Nižšie je uvedený kód štvorca. Všimnite si, že štvorec rozširuje obdĺžnik.

public class Square extends Rectangle {public void setBreadth (int width) {super.setBreadth (width) super.setLength (width)} public void setLength (int length) {super.setLength (length) super.setBreadth (length)}}

V tomto prípade sa pokúsime vytvoriť vzťah ISA medzi štvorcom a obdĺžnikom tak, že volanie „štvorec je obdĺžnik“ v nasledujúcom kóde by sa začalo neočakávane správať, ak dôjde k odovzdaniu inštancie štvorca. V prípade kontroly „Oblasti“ a kontroly „Šírky“ bude vyhodená chyba tvrdenia, program sa však ukončí, pretože chyba vháňania bude vyhodená z dôvodu zlyhania kontroly oblasti.

public class LSPDemo {public void CalcArea (Rectangle r) {r.setBreadth (2) r.setLength (3) assert 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čakávaná hodnota' + errorIdentifer + ' napríklad '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // Predá sa inštancia obdĺžnika lsp.calculateArea (nový obdĺžnik ()) // Inštancia Square je odovzdaná lsp.calculateArea (new Square ())}}

Trieda demonštruje princíp substitúcie Liskov (LSP) Podľa princípu musia byť funkcie, ktoré používajú odkazy na základné triedy, schopné používať objekty odvodenej triedy bez toho, aby o tom vedeli.

V príklade zobrazenom nižšie by teda funkcia CalcArea, ktorá používa odkaz „Obdĺžnik“, mala byť schopná používať objekty odvodenej triedy, ako je napríklad štvorec, a splniť požiadavku stanovenú v definícii obdĺžnika. Je potrebné poznamenať, že podľa definície obdĺžnika musí vždy platiť nasledovné:

  1. Dĺžka sa musí vždy rovnať dĺžke prekonanej ako vstup do metódy setLength
  2. Šírka sa musí vždy rovnať šírke zadanej ako vstup do metódy setBreadth
  3. Plocha musí byť vždy rovnaká ako súčin dĺžky a šírky

V prípade, že sa pokúsime nadviazať vzťah ISA medzi štvorcom a obdĺžnikom tak, že ho nazveme „štvorec je obdĺžnik“, vyššie uvedený kód by sa začal správať neočakávane, ak dôjde k odovzdaniu inštancie štvorca. V prípade kontroly oblasti a kontroly bude vyhodená chyba tvrdenia pre šírku, aj keď sa program ukončí, pretože je vyhodená chyba tvrdenia z dôvodu zlyhania kontroly oblasti.

Trieda Square nepotrebuje metódy ako setBreadth alebo setLength. Trieda LSPDemo by potrebovala poznať podrobnosti odvodených tried obdĺžnika (napríklad štvorec), aby mohla správne kódovať, aby sa vyhla chybe pri hádzaní. Zmena v existujúcom kódexe porušuje predovšetkým princíp otvoreného a zatvoreného.

Princíp segregácie rozhrania

Robert C. Martin to popisuje tak, že klienti by nemali byť nútení zavádzať zbytočné metódy, ktoré nebudú používať.

PodľaPrincíp segregácie rozhraniaklienta, bez ohľadu na to, čo by nikdy nemalo byť nútené implementovať rozhranie, ktoré nepoužíva, alebo by klient nikdy nemal byť povinný závisieť od akejkoľvek metódy, ktorú nepoužívajú. Zásady segregácie rozhrania teda v zásade uprednostňujú rozhrania, ktoré sú malé, ale špecifické pre klienta namiesto monolitického a väčšieho rozhrania. Stručne povedané, bolo by zlé, keby ste klienta prinútili závisieť od určitej veci, ktorú nepotrebuje.

Napríklad jediné protokolovacie rozhranie na zápis a čítanie protokolov je užitočné pre databázu, ale nie pre konzolu. Čítanie protokolov nemá pre záznamník konzoly zmysel. Pokračujeme týmto článkom SOLID Princípy v Jave.

Prečo je táto zásada požadovaná?

Povedzme, že existuje rozhranie Reštaurácie, ktoré obsahuje spôsoby prijímania objednávok od zákazníkov online, zákazníkov telefonického alebo telefonického spojenia a externých zákazníkov. Obsahuje tiež spôsoby spracovania online platieb (pre online zákazníkov) a osobných platieb (pre bežných zákazníkov, ako aj pre zákazníkov po telefóne, keď sú ich objednávky doručené doma).

Teraz vytvorme rozhranie Java pre reštauráciu a pomenujeme ju ako RestaurantInterface.java.

verejné rozhranie RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}

V RestaurantInterface je definovaných 5 metód, ktoré slúžia na prijatie objednávky online, uskutočnenie telefonickej objednávky, prijatie objednávky od vstupujúceho zákazníka, prijatie platby online a prijatie platby osobne.

Začnime implementáciou RestaurantInterface pre online zákazníkov ako OnlineClientImpl.java

verejná trieda OnlineClientImpl implementuje RestaurantInterface {public void acceptOnlineOrder () {// logika pre zadanie online objednávky} public void takeTelephoneOrder () {// neplatí pre online objednávku hodiť novú UnsupportedOperationException ()} public void payOnline () {// logika pre platenie online} public void walkInCustomerOrder () {// Neaplikovateľné pre online objednávku hodiť nové UnsupportedOperationException ()} public void payInPerson () {// Neaplikovateľné pre online objednávku hodiť nové UnsupportedOperationException ()}}
  • Pretože vyššie uvedený kód (OnlineClientImpl.java) je pre online objednávky, vyhoďte UnsupportedOperationException.

  • Online, telefonickí a prichádzajúci klienti používajú implementáciu RestaurantInterface špecifickú pre každého z nich.

  • Triedy implementácie pre telefónneho klienta a klienta typu Walk-in budú mať nepodporované metódy.

  • Pretože 5 metód je súčasťou RestaurantInterface, implementačné triedy musia implementovať všetkých 5 z nich.

  • Metódy, ktoré každá z implementačných tried hodí UnsupportedOperationException. Ako môžete jasne vidieť - implementácia všetkých metód je neefektívna.

  • Akákoľvek zmena v ktorejkoľvek z metód RestaurantInterface sa rozšíri do všetkých implementačných tried. Údržba kódu potom začne byť skutočne ťažkopádna a regresné účinky zmien sa budú stále zvyšovať.

    návody pre Visual Studio pre začiatočníkov
  • RestaurantInterface.java porušuje princíp jedinej zodpovednosti, pretože logika platieb aj logiky zadávania objednávok je zoskupená do jedného rozhrania.

Na prekonanie vyššie spomenutých problémov aplikujeme princíp segregácie rozhraní na refaktorovanie vyššie uvedeného dizajnu.

  1. Funkcie platieb a objednávok môžete rozdeliť do dvoch samostatných štíhlych rozhraní, PaymentInterface.java a OrderInterface.java.

  2. Každý z klientov používa po jednej implementácii PaymentInterface a OrderInterface. Napríklad - OnlineClient.java používa OnlinePaymentImpl a OnlineOrderImpl atď.

  3. Princíp jednej zodpovednosti je teraz pripojený ako platobné rozhranie (PaymentInterface.java) a objednávkové rozhranie (OrderInterface).

  4. Zmena v ktoromkoľvek z rozhraní objednávky alebo platby nemá vplyv na druhé. Teraz sú nezávislé. Nebude potrebné robiť žiadnu fiktívnu implementáciu ani hádzať UnsupportedOperationException, pretože každé rozhranie má iba metódy, ktoré bude vždy používať.

Po aplikácii ISP

Princíp inverzie závislostí

Robert C. Martin to popisuje, pretože to závisí od abstrakcií, nie od konkrétností. Podľa neho sa modul na vysokej úrovni nikdy nesmie spoliehať na žiadny modul na nízkej úrovni. napríklad

Prejdete do miestneho obchodu, aby ste niečo kúpili, a rozhodnete sa za to zaplatiť debetnou kartou. Keď teda odovzdáte svoju kartu úradníkovi na uskutočnenie platby, úradník sa neobťažuje skontrolovať, aký druh karty ste mu dali.

Aj keď ste dali kartu Visa, nevydá automat Visa na prejdenie vašej karty. Na type kreditnej alebo debetnej karty, ktorú máte na zaplatenie, nezáleží, jednoducho ju prejdú prstom. Na tomto príklade teda môžete vidieť, že vy aj úradník ste závislí od abstrakcie kreditnej karty a že sa neobávate špecifík karty. To je to, čo je princíp inverzie závislostí.

Prečo je táto zásada požadovaná?

Umožňuje programátorovi odstrániť pevne zakódované závislosti, aby sa aplikácia stala voľne spojenou a rozšíriteľnou.

public class Student {private Address address public Student () {address = new Address ()}}

Vo vyššie uvedenom príklade vyžaduje trieda Student objekt Address a je zodpovedný za inicializáciu a použitie objektu Address. Ak sa v budúcnosti zmení trieda adresy, musíme urobiť zmeny aj v študentskej triede. Toto umožňuje tesné spojenie medzi objektmi Student a Address. Tento problém môžeme vyriešiť pomocou návrhového vzoru inverzie závislostí. tj. Objekt adresy bude implementovaný nezávisle a bude poskytnutý Študentovi, keď bude Študent inštancovaný pomocou inverzie závislostí založenej na konštruktore alebo nastavovači.

Týmto prichádzame na koniec týchto SOLID Princípov v Jave.

Pozrite sa na autor: Edureka, dôveryhodná online vzdelávacia spoločnosť so sieťou viac ako 250 000 spokojných študentov rozmiestnených po celom svete. Výcvikový a certifikačný kurz Edureka Java J2EE a SOA je určený pre študentov a profesionálov, ktorí chcú byť vývojármi Java. Kurz je navrhnutý tak, aby vám dal náskok v programovaní v jazyku Java a naučil vás základné aj pokročilé koncepty jazyka Java spolu s rôznymi rámcami Java, ako je Hibernate & Spring.

Máte na nás otázku? Uveďte to prosím v sekcii komentárov v tomto blogu „SOLID Principles in Java“ a my sa vám ozveme čo najskôr.