Vše, co potřebujete vědět o pevných principech v Javě



V tomto článku se dozvíte podrobně o tom, co jsou Solid Principy v Javě, s příklady a jejich důležitostí s příkladem z reálného života.

Ve světě (OOP), existuje mnoho návrhových pokynů, vzorů nebo zásad. Pět z těchto principů je obvykle seskupeno a je známo pod zkratkou SOLID. I když každý z těchto pěti principů popisuje něco konkrétního, překrývají se také tak, že přijetí jednoho z nich implikuje nebo vede k přijetí jiného. V tomto článku porozumíme principům SOLID v Javě.

Historie principů SOLID v Javě

Robert C. Martin uvedl pět objektově orientovaných principů návrhu a používá se pro něj zkratka „S.O.L.I.D“. Když kombinovaným způsobem využijete všechny principy S.O.L.I.D, bude pro vás snazší vyvíjet software, který lze snadno spravovat. Mezi další funkce používání S.O.L.I.D patří:





  • Zabraňuje pachům kódu.
  • Rychle kód refraktoru.
  • Umí adaptivní nebo agilní vývoj softwaru.

Když při kódování použijete princip S.O.L.I.D, začnete psát kód, který je efektivní a efektivní.



Jaký je význam S.O.L.I.D?

Solid představuje pět principů Java, kterými jsou:

  • S : Princip jediné odpovědnosti
  • NEBO : Princip otevřeno-zavřeno
  • L : Princip substituce Liskov
  • : Princip segregace rozhraní
  • D : Princip inverze závislostí

V tomto blogu se budeme podrobně věnovat všem pěti zásadám SOLID Java.



Princip jediné odpovědnosti v Javě

Co to říká?

Robert C. Martin to popisuje tak, že jedna třída by měla mít pouze jednu a jedinou odpovědnost.

Podle zásady jediné odpovědnosti by měl existovat pouze jeden důvod, kvůli kterému musí být třída změněna. To znamená, že třída by měla mít jeden úkol. Tento princip se často označuje jako subjektivní.

co je MVC v Javě

Princip lze dobře pochopit na příkladu. Představte si, že existuje třída, která provádí následující operace.

  • Připojeno k databázi

  • Načíst některá data z databázových tabulek

  • Nakonec jej zapište do souboru.

Představili jste si scénář? Tady má třída několik důvodů ke změně, a jen málo z nich je úprava výstupu souboru, přijetí nové databáze. Když mluvíme o odpovědnosti za jedinou zásadu, řekli bychom, že existuje příliš mnoho důvodů, proč se třída může změnit, proto to do zásady jednotné odpovědnosti nepatří správně.

Například třída automobilu se může spustit nebo zastavit sama, ale úkol umýt ji patří do třídy CarWash. V jiném příkladu má Book třída vlastnosti pro uložení vlastního jména a textu. Úkol tisku knihy však musí patřit do třídy tiskárny knih. Třída tiskárny knih může tisknout na konzolu nebo jiné médium, ale takové závislosti jsou z třídy knihy odstraněny

Proč je tento princip vyžadován?

Při dodržování principu jediné odpovědnosti je testování snazší. S jedinou odpovědností bude mít třída méně testovacích případů. Méně funkcí také znamená méně závislostí na jiných třídách. Vede to k lepší organizaci kódu, protože menší a dobře navržené třídy se snáze vyhledávají.

Příklad k objasnění této zásady:

Předpokládejme, že budete požádáni o implementaci služby UserSetting, ve které může uživatel změnit nastavení, ale předtím musí být uživatel ověřen. Jedním ze způsobů, jak to provést, by bylo:

public class UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// Grant option to change}} public boolean checkAccess (User user) {// Ověřte, zda je uživatel platný. }}

Vše vypadá dobře, dokud nebudete chtít znovu použít kód checkAccess na jiném místě NEBO chcete provést změny ve způsobu, jakým se provádí checkAccess. Ve všech 2 případech byste nakonec změnili stejnou třídu a v prvním případě byste museli použít UserSettingService také ke kontrole přístupu.
Jedním ze způsobů, jak to opravit, je rozložit UserSettingService na UserSettingService a SecurityService. A přesuň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. }}

Otevřete uzavřený princip v Javě

Robert C. Martin to popisuje jako softwarové komponenty by měly být otevřené pro rozšíření, ale uzavřené pro modifikaci.

Abych byl přesný, podle tohoto principu by měla být třída napsána takovým způsobem, že vykonává svou práci bezchybně bez předpokladu, že lidé v budoucnu jednoduše přijdou a změní ji. Třída by proto měla zůstat uzavřená kvůli úpravám, ale měla by mít možnost se rozšířit. Způsoby rozšíření třídy zahrnují:

  • Dědění z třídy

  • Přepsání požadovaného chování třídy

  • Rozšíření určitého chování třídy

Vynikající příklad principu otevřeno-zavřeno lze pochopit pomocí prohlížečů. Pamatujete si, že jste si do prohlížeče Chrome nainstalovali rozšíření?

Základní funkcí prohlížeče Chrome je procházet různé weby. Chcete zkontrolovat gramatiku při psaní e-mailu pomocí prohlížeče Chrome? Pokud ano, můžete jednoduše použít rozšíření Grammarly, které vám umožní kontrolu gramatiky obsahu.

Tento mechanismus, do kterého přidáváte věci pro zvýšení funkčnosti prohlížeče, je rozšířením. Prohlížeč je tedy dokonalým příkladem funkce, která je otevřená pro rozšíření, ale je uzavřena pro úpravy. Jednoduše řečeno, můžete vylepšit funkčnost přidáním / instalací pluginů do vašeho prohlížeče, ale nemůžete vytvářet nic nového.

Proč je tento princip vyžadován?

OCP je důležité, protože třídy k nám mohou přicházet prostřednictvím knihoven třetích stran. Měli bychom být schopni rozšířit tyto třídy bez obav, pokud tyto základní třídy mohou podporovat naše rozšíření. Ale dědičnost může vést k podtřídám, které závisí na implementaci základní třídy. Aby se tomu zabránilo, doporučuje se použít rozhraní. Tato dodatečná abstrakce vede k uvolněné vazbě.

Řekněme, že musíme vypočítat oblasti různých tvarů. Začneme vytvořením třídy pro náš první tvar Obdélníkkterý má 2 atributy délky& šířka.

public class Rectangle {public double length public double width}

Dále vytvoříme třídu pro výpočet plochy tohoto obdélníkukterý má metodu spočítatRectangleAreakterý bere obdélníkjako vstupní parametr a vypočítá jeho plochu.

veřejná třída AreaCalculator {veřejné dvojité počítatRectangleArea (obdélník obdélník) {návrat obdélník.délka * obdélník. šířka}}

Zatím je vše dobré. Řekněme, že dostaneme náš druhý tvarový kruh. Okamžitě tedy vytvoříme novou třídu Circles jediným poloměrem atributu.

public class Circle {public double radius}

Poté upravíme Areacalculatortřída pro přidání výpočtů kružnice pomocí nové metody countCircleaArea ()

veřejná třída AreaCalculator {veřejné dvojité spočítatRectangleArea (obdélník obdélník) {návrat obdélník.délka * obdélník.šířka} veřejné zdvojnásobit výpočetCircleArea (kruh kruh) {návrat (22/7) * kruh.radius * kruh.radius}}

Všimněte si však, že ve způsobu, jakým jsme navrhli naše řešení výše, byly nedostatky.

Řekněme, že máme nový tvar pětiúhelníku. V takovém případě opět skončíme s úpravou třídy AreaCalculator. Jak typy tvarů rostou, stává se to chaotičtějším, protože AreaCalculator se neustále mění a všichni spotřebitelé této třídy budou muset neustále aktualizovat své knihovny, které obsahují AreaCalculator. Výsledkem bude, že třída AreaCalculator nebude baselined (finalizována) se zárukou, protože pokaždé, když přijde nový tvar, bude upraven. Takže tento design není uzavřen pro úpravy.

AreaCalculator bude muset nadále přidávat svou výpočetní logiku v novějších metodách. Ve skutečnosti nerozšiřujeme rozsah tvarů, spíše děláme řešení kousek po kousku pro každý přidaný tvar.

Úprava výše uvedeného designu v souladu s principem otevřeno / zavřeno:

Podívejme se nyní na elegantnější design, který řeší nedostatky ve výše uvedeném designu dodržováním principu otevřeno / zavřeno. Nejprve uděláme design rozšiřitelným. Za tímto účelem musíme nejprve definovat základní typ Shape a mít Circle & Rectangle implementovat Shape interface.

veřejné rozhraní Shape {public double CalcArea ()} veřejná třída Obdélník implementuje Shape {dvojitá délka dvojitá šířka public double CalcArea () {návratová délka * šířka}} veřejná třída Circle implementuje Shape {public double radius public double CalcArea () {return (22 / 7) * poloměr * poloměr}}

K dispozici je základní rozhraní Tvar. Všechny tvary nyní implementují základní rozhraní Shape. Tvarové rozhraní má abstraktní metodu CalculateArea (). Kruh i obdélník poskytují vlastní přepsanou implementaci metody CalcArea () pomocí vlastních atributů.
Přinesli jsme stupeň rozšiřitelnosti, protože tvary jsou nyní instancí rozhraní Shape. To nám umožňuje používat Shape místo jednotlivých tříd
Poslední bod výše zmínil spotřebitele těchto tvarů. V našem případě bude spotřebitelem třída AreaCalculator, která by nyní vypadala takto.

veřejná třída AreaCalculator {veřejné dvojité spočítatShapeArea (tvar tvaru) {vrátit tvar.calculateArea ()}}

Tento AreaCalculatortřída nyní plně odstraňuje naše konstrukční chyby uvedené výše a poskytuje čisté řešení, které dodržuje princip otevřeno-zavřeno. Pojďme s dalšími principy SOLID v Javě

Princip náhrady Liskov v Javě

Robert C. Martin to popisuje jako odvozené typy musí být zcela zastupitelné pro své základní typy.

Princip substituce Liskov předpokládá, že q (x) je vlastnost prokazatelná u entit x, která patří k typu T. Nyní by podle tohoto principu mělo být q (y) nyní prokazatelné pro objekty y, které patří k typu S, a S je vlastně subtyp T. Jsi teď zmatený a nevíš, co vlastně znamená Liskovův substituční princip? Jeho definice může být trochu složitá, ale ve skutečnosti je to docela snadné. Jediná věc je, že každá podtřída nebo odvozená třída by měla být zaměnitelná za svou nadřazenou nebo základní třídu.

Můžete říci, že se jedná o jedinečný objektově orientovaný princip. Princip lze dále zjednodušit dětským typem konkrétního nadřazeného typu, aniž by došlo ke komplikacím, nebo by se mělo vyhodit do povětří, že má schopnost zastupovat tohoto rodiče. Tento princip úzce souvisí s principem Liskovovy substituce.

Proč je tento princip vyžadován?

Tím se zabrání zneužití dědictví. Pomáhá nám přizpůsobit se vztahu „je-a“. Můžeme také říci, že podtřídy musí splňovat kontrakt definovaný základní třídou. V tomto smyslu to souvisí sNávrh podle smlouvyto poprvé popsal Bertrand Meyer. Například je lákavé říci, že kruh je druh elipsy, ale kruhy nemají dvě ohniska ani hlavní / vedlejší osy.

LSP je populárně vysvětlen pomocí příkladu čtverce a obdélníku. pokud předpokládáme vztah ISA mezi čtvercem a obdélníkem. Říkáme tedy „Čtverec je obdélník.“ Níže uvedený kód představuje vztah.

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 () {návrat šíře} public void setBreadth (int šířka) { this.breadth = šířka} public int getArea () {vrátit this.length * this.breadth}}

Níže je uveden kód pro Square. Všimněte si, že čtverec rozšiřuje obdélník.

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 případě se pokusíme navázat vztah ISA mezi čtvercem a obdélníkem tak, že volání „Čtverec je obdélník“ v níže uvedeném kódu by se začalo chovat neočekávaně, pokud je předána instance Square. Chyba kontroly bude vyvolána v případě kontroly „Plochy“ a kontroly „Šířky“, i když program bude ukončen, protože chyba kontroly bude vyvolána kvůli selhání 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čekávaná hodnota' + errorIdentifer + ' například '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // Předává se instance Rectangle lsp.calculateArea (new Rectangle ()) // Je předána instance Square lsp.calculateArea (new Square ())}}

Třída demonstruje Liskovův substituční princip (LSP) Podle principu musí být funkce, které používají odkazy na základní třídy, schopny používat objekty odvozené třídy, aniž by o tom věděly.

V níže uvedeném příkladu by tedy funkce CalcArea, která používá odkaz „Obdélník“, měla být schopna používat objekty odvozené třídy, jako je Čtverec, a splnit požadavek vyplývající z definice Obdélník. Je třeba poznamenat, že podle definice obdélníku musí vždy platit následující vzhledem k níže uvedeným údajům:

  1. Délka se musí vždy rovnat délce předané jako vstup metodě, setLength
  2. Šířka se musí vždy rovnat šířce předané jako vstup metodě setBreadth
  3. Plocha musí být vždy rovna součinu délky a šířky

V případě, že se pokusíme navázat vztah ISA mezi čtvercem a obdélníkem tak, že nazýváme „čtverec je obdélník“, výše uvedený kód by se začal chovat neočekávaně, pokud je předána instance čtverce. V případě kontroly oblasti a kontroly bude vyvolána chyba tvrzení pro šířku, i když program bude ukončen, protože je vyhozena chyba tvrzení kvůli selhání kontroly oblasti.

Třída Square nepotřebuje metody jako setBreadth nebo setLength. Třída LSPDemo by potřebovala znát podrobnosti odvozených tříd Rectangle (například Square), aby správně kódovala, aby nedocházelo k chybě při házení. Změna stávajícího kódu na prvním místě rozbíjí princip otevřeno-zavřeno.

Princip segregace rozhraní

Robert C. Martin to popisuje tak, že by klienti neměli být nuceni zavádět zbytečné metody, které nebudou používat.

PodlePrincip segregace rozhraníklient, bez ohledu na to, co by nikdy nemělo být nuceno implementovat rozhraní, které nepoužívá, nebo by klient nikdy neměl být povinen záviset na jakékoli metodě, kterou nepoužívají. Zásady segregace rozhraní tedy v zásadě rozhraní, která jsou malá, ale specifická pro klienta namísto monolitického a většího rozhraní. Stručně řečeno, bylo by špatné, kdybyste klienta přinutili záviset na určité věci, kterou nepotřebují.

Například jediné protokolovací rozhraní pro zápis a čtení protokolů je užitečné pro databázi, ale ne pro konzolu. Čtení protokolů nemá pro záznamník konzoly smysl. Pokračování v tomto článku SOLID Principles in Java.

Proč je tento princip vyžadován?

Řekněme, že existuje rozhraní restaurace, které obsahuje způsoby přijímání objednávek od online zákazníků, telefonických nebo telefonických zákazníků a odchozích zákazníků. Obsahuje také způsoby zpracování online plateb (pro online zákazníky) a osobních plateb (pro odchozí i telefonické zákazníky, pokud je jejich objednávka doručena doma).

Nyní vytvořme rozhraní Java pro restauraci a pojmenujeme ji jako RestaurantInterface.java.

veřejné rozhraní RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}

V RestaurantInterface je definováno 5 metod, které slouží k přijímání online objednávek, přijímání telefonických objednávek, přijímání objednávek od vstupujícího zákazníka, přijímání online plateb a přijímání plateb osobně.

Začněme implementací RestaurantInterface pro online zákazníky jako OnlineClientImpl.java

veřejná třída OnlineClientImpl implementuje RestaurantInterface {public void acceptOnlineOrder () {// logika pro zadávání online objednávky} public void takeTelephoneOrder () {// Nelze použít pro online objednávku hodit novou UnsupportedOperationException ()} public void payOnline () {// logika pro platby online} public void walkInCustomerOrder () {// Nelze použít pro online objednávku hodit novou UnsupportedOperationException ()} public void payInPerson () {// Nelze použít pro online objednávku hodit novou UnsupportedOperationException ()}}
  • Vzhledem k tomu, že výše uvedený kód (OnlineClientImpl.java) je pro online objednávky, hodí UnsupportedOperationException.

  • Online, telefonní a odchozí klienti používají implementaci RestaurantInterface specifickou pro každého z nich.

  • Implementační třídy pro klienta Telephonic a Walk-in klienta budou mít nepodporované metody.

  • Protože 5 metod je součástí RestaurantInterface, implementační třídy musí implementovat všech 5 z nich.

  • Metody, které každá z implementačních tříd vyvolá UnsupportedOperationException. Jak jasně vidíte - implementace všech metod je neefektivní.

  • Jakákoli změna v kterékoli z metod RestaurantInterface bude přenesena do všech implementačních tříd. Údržba kódu pak začíná být opravdu těžkopádná a regresní efekty změn se budou stále zvyšovat.

    jak používat balíček v javě
  • RestaurantInterface.java porušuje princip jediné odpovědnosti, protože logika pro platby i logiku pro zadávání objednávek je seskupena do jednoho rozhraní.

Abychom překonali výše uvedené problémy, aplikujeme Princip segregace rozhraní k refaktorování výše uvedeného designu.

  1. Oddělte funkce platby a umístění objednávky do dvou samostatných štíhlých rozhraní, PaymentInterface.java a OrderInterface.java.

  2. Každý z klientů používá po jedné implementaci PaymentInterface a OrderInterface. Například - OnlineClient.java používá OnlinePaymentImpl a OnlineOrderImpl atd.

  3. Princip jediné odpovědnosti je nyní připojen jako platební rozhraní (PaymentInterface.java) a objednávkové rozhraní (OrderInterface).

  4. Změna v kterémkoli z rozhraní objednávky nebo platby nemá vliv na druhé. Nyní jsou nezávislé. Nebude nutné provádět žádné fiktivní implementace ani házet UnsupportedOperationException, protože každé rozhraní má pouze metody, které bude vždy používat.

Po aplikaci ISP

Princip inverze závislosti

Robert C. Martin to popisuje, protože záleží na abstrakcích, nikoli na konkrementech. Podle něj se modul na vysoké úrovni nikdy nesmí spoléhat na žádný modul na nízké úrovni. například

Jdete do místního obchodu, abyste něco koupili, a rozhodnete se to zaplatit pomocí debetní karty. Když tedy předáte svou kartu prodávajícímu k provedení platby, úředník se neobtěžuje zkontrolovat, jaký druh karty jste dali.

I když jste dali kartu Visa, nevydá stroj Visa pro přejetí vaší karty. Na typu kreditní nebo debetní karty, kterou máte k zaplacení, nezáleží, jednoduše ji přejedete prstem. V tomto příkladu tedy vidíte, že vy i prodavač jste závislí na abstrakci kreditní karty a nemusíte se starat o specifika karty. To je to, co je princip inverze závislostí.

Proč je tento princip vyžadován?

Umožňuje programátorovi odstranit pevně zakódované závislosti, aby se aplikace stala volně spojenou a rozšiřitelnou.

veřejná třída Student {soukromá adresa adresa public Student () {adresa = nová adresa ()}}

Ve výše uvedeném příkladu vyžaduje třída Student objekt Address a je zodpovědná za inicializaci a použití objektu Address. Pokud se třída Adresa v budoucnu změní, musíme provést změny také ve třídě Student. Tím se vytvoří těsné propojení mezi objekty Student a Address. Tento problém můžeme vyřešit pomocí návrhového vzoru inverze závislostí. tj. objekt adresy bude implementován samostatně a bude poskytnut Studentovi, když bude Student vytvořen pomocí inverze závislostí na základě konstruktoru nebo setteru.

S tím se dostáváme na konec těchto SOLID Principles v Javě.

Podívejte se na Edureka, důvěryhodná online vzdělávací společnost se sítí více než 250 000 spokojených studentů po celém světě. Školicí a certifikační kurz Edureka Java J2EE a SOA je určen pro studenty a profesionály, kteří chtějí být vývojářem Java. Kurz je navržen tak, aby vám poskytl náskok v programování v Javě a naučil vás základní i pokročilé koncepty Javy spolu s různými jávskými rámci, jako je Hibernate & Spring.

Máte na nás dotaz? Uveďte to prosím v sekci komentářů v tomto blogu „SOLID Principles in Java“ a my se vám ozveme co nejdříve.