Minden, amit tudnia kell a Java alapelveiről

Ebben a cikkben részletesen megtudhatja, hogy mik a szilárd alapelvek a java-ban, példákkal és azok fontosságával a valós élet példáival.

Világában (OOP), számos tervezési irányelv, minta vagy elv létezik. Ezen elvek közül öt általában csoportosul, és a SOLID rövidítéssel ismertek. Míg az öt alapelv mindegyike leír valami konkrétat, átfedésben vannak, és egyikük elfogadása egy másik elfogadásához vezet vagy vezet. Ebben a cikkben megértjük a SOLID alapelveket a Java-ban.



A SOLID alapelvek története a Java-ban

Robert C. Martin öt objektum-orientált tervezési alapelvet adott meg, és ehhez az „S.O.L.I.D” rövidítést használják. Ha az S.O.L.I.D összes alapelvét kombináltan alkalmazza, könnyebbé válik a könnyen kezelhető szoftverek kifejlesztése. Az S.O.L.I.D használatának további jellemzői:



  • Kerüli a kódszagokat.
  • Gyorsan fénytörő kód.
  • Képes adaptív vagy agilis szoftverfejlesztésre.

Amikor az S.O.L.I.D elvét használja a kódolásában, elkezdi írni a hatékony és eredményes kódot.



Mit jelent az S.O.L.I.D?

A Solid a java öt elvét képviseli:

  • S : Egyetlen felelősség elve
  • VAGY : Nyitott-zárt elv
  • L : Liskov helyettesítési elv
  • én : Interfész elkülönítési elv
  • D : Függőségi inverzió elve

Ebben a blogban részletesen tárgyaljuk a Java mind az öt SOLID elvét.



Egyetlen felelősség elve a Java-ban

Mit mond?

Robert C. Martin úgy írja le, hogy egy osztálynak csak egyetlen felelőssége kell, hogy legyen.

Az egyetlen felelősség elve szerint csak egyetlen oka lehet annak, ami miatt osztályt kell változtatni. Ez azt jelenti, hogy egy osztálynak egyetlen feladatot kell elvégeznie. Ezt az elvet gyakran szubjektívnek nevezik.

Az elv egy példával jól érthető. Képzelje el, hogy van egy osztály, amely a következő műveleteket hajtja végre.

  • Csatlakoztatva egy adatbázishoz

  • Olvasson el néhány adatot az adatbázis-táblákból

  • Végül írjon egy fájlba.

Elképzelte a forgatókönyvet? Itt az osztálynak több oka van a változtatásra, és közülük kevés a fájlkimenet módosítása, új adatbázis-elfogadás. Amikor egyetlen alapelvű felelősségről beszélünk, azt mondanánk, hogy túl sok oka van annak, hogy az osztály ezért megváltozzon, ez nem illik megfelelően az egyetlen felelősség elvébe.

Például egy Automobile osztály elindíthatja vagy leállíthatja önmagát, de annak mosása a CarWash osztályba tartozik. Egy másik példában a Book osztály rendelkezik saját név és szöveg tárolására alkalmas tulajdonságokkal. De a könyv kinyomtatásának a Könyvnyomtató osztályba kell tartoznia. Lehet, hogy a Könyvnyomtató osztály konzolra vagy más adathordozóra nyomtat, de az ilyen függőségeket eltávolítja a Könyvosztályból

Miért van szükség erre az elvre?

Az egységes felelősség elvének betartása esetén a tesztelés könnyebb. Egyetlen felelősséggel az osztálynak kevesebb tesztesete lesz. A kevesebb funkcionalitás kevesebb függőséget jelent más osztályoktól is. Jobb kódszervezéshez vezet, mivel a kisebb és jól célzott osztályok könnyebben kereshetők.

Példa ezen elv tisztázására:

Tegyük fel, hogy egy UserSetting szolgáltatás bevezetését kéri, amelyben a felhasználó megváltoztathatja a beállításokat, de előtte hitelesíteni kell a felhasználót. Ennek egyik módja a következő lenne:

public class UserSettingService {public void changeEmail (User user) {if (checkAccess (user)) {// A változtatás megadásának opciója}} public boolean checkAccess (User user) {// Ellenőrizze, hogy a felhasználó érvényes-e. }}

Mindegyik jól néz ki, amíg nem szeretné újra használni a checkAccess kódot valamilyen más helyen, VAGY módosítani szeretné a checkAccess kódját. Mind a 2 esetben végül ugyanazt az osztályt változtatja meg, és az első esetben a UserSettingService szolgáltatást kell használnia a hozzáférés ellenőrzéséhez is.
Ennek egyik módja a UserSettingService bontása UserSettingService és SecurityService-be. És vigye az checkAccess kódot a SecurityService-ba.

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) {// ellenőrizze a hozzáférést. }}

Nyissa meg a zárt elvet a Java-ban

Robert C. Martin leírja, hogy a szoftverkomponenseknek nyitva kell lenniük a kiterjesztéshez, de a módosításokhoz bezárva.

Pontosabban: ezen elv szerint egy osztályt úgy kell írni, hogy hibátlanul végezze a munkáját anélkül, hogy feltételezné, hogy az emberek a jövőben egyszerűen eljönnek és megváltoztatják. Ennélfogva az osztálynak változatlanul zárva kell maradnia, de lehetősége van a kiterjesztésre. Az osztály kiterjesztésének módjai a következők:

  • Öröklődik az osztályból

  • A szükséges viselkedésmódok felülírása az osztálytól

  • Az osztály bizonyos viselkedésének kiterjesztése

A nyitott-zárt elv kiváló példája a böngészők segítségével érthető meg. Emlékszel a kiterjesztések telepítésére a Chrome böngészőbe?

A Chrome böngésző alapvető funkciója a különböző webhelyeken való böngészés. Szeretné ellenőrizni a nyelvtant, amikor e-mailt ír a chrome böngészővel? Ha igen, egyszerűen használhatja a Grammarly kiterjesztést, ez biztosítja a nyelvtan ellenőrzését a tartalomon.

Ez a mechanizmus, amelyhez a böngésző funkcionalitásának növelése érdekében hozzáad egy dolgot, kiterjesztés. Ennélfogva a böngésző tökéletes példa a funkcionalitásra, amely nyitva áll a kiterjesztéshez, de zárva van a módosítás érdekében. Egyszerű szavakkal bővítheti a funkcionalitást bővítmények hozzáadásával / telepítésével a böngészőbe, de semmi újat nem építhet.

Miért van szükség erre az elvre?

Az OCP fontos, mivel az osztályok harmadik fél könyvtárain keresztül érkezhetnek hozzánk. Képesnek kell lennünk kibővíteni ezeket az osztályokat anélkül, hogy aggódnánk, ha ezek az alaposztályok támogatni tudják a bővítményeinket. Az öröklés azonban olyan alosztályokhoz vezethet, amelyek az alaposztály megvalósításától függenek. Ennek elkerülése érdekében az interfészek használata ajánlott. Ez a további absztrakció laza összekapcsolódáshoz vezet.

Mondjuk, különféle alakú területeket kell kiszámítanunk. Kezdjük azzal, hogy létrehozunk egy osztályt az első formájú Téglalap számáraamelynek 2 attribútumhossza van& szélesség.

public class Téglalap {public double length public double width}

Ezután létrehozunk egy osztályt a téglalap területének kiszámításáhozaminek van egy methodRectangleArea módszereamely a Téglalapot veszibemeneti paraméterként és kiszámítja annak területét.

public class AreaCalculator {nyilvános kettős kiszámításRectangleArea (téglalap téglalap) {return rectangle.length * rectangle.width}}

Eddig jó. Most tegyük fel, hogy megkapjuk a második alak körünket. Így azonnal létrehozunk egy új osztály körtegyetlen attribútum sugárral.

nyilvános osztály Kör {nyilvános kettős sugár}

Ezután módosítjuk a Areacalculator-tosztály a körszámítások hozzáadásához egy új módszer segítségével calcCircleaArea ()

public class AreaCalculator {nyilvános kettős calcRectangleArea (Négyszög téglalap) {return téglalap.hossz * téglalap.szélesség} public double apskaiCircleArea (Kör kör) {return (22/7) * kör.radius * kör.radius}}

Azonban vegye figyelembe, hogy a fenti megoldásunk megtervezésében voltak hibák.

Mondjuk, hogy új alakú ötszögünk van. Ebben az esetben ismét a AreaCalculator osztályt módosítjuk. Amint az alakzatok típusa növekszik, ez egyre szentségesebbé válik, mivel a AreaCalculator folyamatosan változik, és az ebbe az osztályba tartozó összes fogyasztónak folyamatosan frissítenie kell a AreaCalculator-t tartalmazó könyvtárakat. Ennek eredményeként a AreaCalculator osztály nem kerül alapra (véglegesítve) kezességgel, mivel minden alkalommal, amikor új alakzat érkezik, ez módosul. Tehát, ez a kialakítás nincs lezárva módosítás céljából.

A AreaCalculator-nak folyamatosan hozzá kell adnia számítási logikáját az újabb módszerekhez. Nem igazán bővítjük a formák körét, hanem egyszerűen darabos (apránként) megoldást csinálunk minden hozzáadott alakra.

A fenti tervezés módosítása a nyitott / zárt elvnek való megfelelés érdekében:

Lássunk most egy elegánsabb dizájnt, amely a nyitott / zárt elv betartásával megoldja a fenti tervezés hibáit. Először bővíteni fogjuk a formatervezést. Ehhez először meg kell határoznunk egy Shape alaptípust, és rendelkeznünk kell a Circle & Rectangle Shape interfésszel.

nyilvános felület Alakzat {nyilvános kettős kiszámításArea ()} nyilvános osztály Téglalap megvalósítások Alakzat {kettős hosszúság kettős szélességű nyilvános kettős kiszámítás terület () {visszatérési hossz * szélesség}} nyilvános osztály Kör valósítja meg Alakzat {nyilvános kettős sugarú nyilvános kettős kiszámítás terület () {visszatérés (22 / 7) * sugár * sugár}}

Van egy alap interfész forma. Minden alakzat megvalósítja az alap felület Shape-t. A Shape felületnek van egy absztrakt módszere a calcArea (). Mind a kör, mind a téglalap biztosítja a saját felülbírált implementálását a calcArea () metódushoz saját attribútumaik felhasználásával.
Hoztunk egy bizonyos kiterjeszthetőséget, mivel a formák ma már a Shape interfészek példái. Ez lehetővé teszi számunkra, hogy a Shape-et használjuk az egyes osztályok helyett
Az utolsó pont a fenti formák fogyasztóit említette. Esetünkben a fogyasztó lesz a AreaCalculator osztály, amely most így nézne ki.

public class AreaCalculator {nyilvános kettős calcShapeArea (Alakzat alakja) {return shape.calculateArea ()}}

Ez a AreaCalculatorosztály most már teljes mértékben kiküszöböli a fent említett tervezési hibáinkat, és tiszta megoldást ad, amely betartja a nyitott-zárt alapelvet. Haladjunk tovább a Java többi SOLID-alapelvével

Liskov helyettesítési elv a Java-ban

Robert C. Martin leírja, hogy a származtatott típusoknak teljesen helyettesíteniük kell az alaptípusaikat.

A Liskov-szubsztitúciós elv feltételezi, hogy a q (x) olyan tulajdonság, amely bizonyítható az x típusú, T típusba tartozó entitások esetében. Most ennek az elvnek megfelelően a q (y) -nek most már bizonyíthatónak kell lennie az S típusba tartozó y objektumok esetében, és az S valójában a T altípusa. Most már összezavarodott, és nem tudja, mit is jelent valójában a Liskov-helyettesítési elv? Lehet, hogy a meghatározása kissé összetett, de valójában meglehetősen egyszerű. Az egyetlen dolog, hogy minden alosztálynak vagy származtatott osztálynak helyettesíteni kell a szülőjét vagy az alaposztályát.

Mondhatjuk, hogy ez egy egyedi objektum-orientált elv. Az elvet tovább egyszerűsítheti egy adott szülőtípus gyermektípusa anélkül, hogy bármilyen komplikáció lenne vagy felrobbantaná a dolgot, képesnek kell lennie arra, hogy kiálljon az adott szülő mellett. Ez az elv szorosan összefügg a Liskov Substitution elvével.

Miért van szükség erre az elvre?

Ezzel elkerülhető az öröklés visszaélése. Ez segít abban, hogy megfeleljünk az „is-a” kapcsolatnak. Azt is mondhatjuk, hogy az alosztályoknak teljesíteniük kell az alaposztály által meghatározott szerződést. Ebben az értelemben összefüggTervezés szerződés szerintezt először Bertrand Meyer írta le. Például csábító azt mondani, hogy a kör egyfajta ellipszis, de a köröknek nincs két fókusa vagy fő / melléktengelye.

Az LSP-t közismerten a négyzet és téglalap példával magyarázzák. ha feltételezünk egy ISA kapcsolatot a Négyzet és a Téglalap között. Így a „négyzet téglalap” -nak nevezzük. Az alábbi kód a kapcsolatot jelöli.

public class Téglalap {private int length private int widthth public int getLength () {return length} public void setLength (int length) {this.length = length} public int getBreadth () {return width} public void setBreadth (int width) { this.breadth = width} public int getArea () {return this.length * this.breadth}}

Az alábbiakban található a Square kódja. Vegye figyelembe, hogy a négyzet kiterjeszti a téglalapot.

public class Square kiterjeszti a téglalapot {public void setBreadth (int szélesség) {super.setBreadth (szélesség) super.setLength (szélesség)} public void setLength (int hossz) {super.setLength (hossz) super.setBreadth (hossz)}}

Ebben az esetben megpróbálunk olyan ISA kapcsolatot kialakítani a Négyzet és a Téglalap között, hogy az alábbi kódban a „Négyzet téglalap” hívás váratlanul kezdjen viselkedni, ha a Négyzet példányát átadják. Megállapítási hiba merül fel a „Terület” és a „Szélesség” ellenőrzése esetén, bár a program leáll, mivel az érvényesítési hiba a területellenőrzés sikertelensége miatt dobódik.

public class LSPDemo {public void calcArea (R téglalap) {r.setBreadth (2) r.setLength (3) állítsa r.getArea () == 6: printError ('area', r) állítsa r.getLength () == 3: printError ('length', r) állít r.getBreadth () == 2: printError ('width', r)} private String printError (String errorIdentifer, R téglalap) {return 'Váratlan értéke' + errorIdentifer + ' például a '+ r.getClass (). getName ()} public static void main (String [] args) {LSPDemo lsp = new LSPDemo () // A téglalap példánya átkerül lsp.calculateArea (új Rectangle ()) // A Square egy példánya át lett adva lsp.calculateArea (new Square ())}}

Az osztály bemutatja a Liskov Substitution Principle-t (LSP) Az alapelvnek megfelelően az alaposztályokra hivatkozásokat használó függvényeknek képeseknek kell lenniük a származtatott osztály objektumainak használatára anélkül, hogy ezt ismernék.

Így az alábbi példában a „Téglalap” hivatkozást használó calcArea függvénynek képesnek kell lennie a származtatott osztály objektumainak, például a Négyzet használatára, és teljesítenie kell a Téglalap definíciója által támasztott követelményeket. Meg kell jegyeznünk, hogy a Téglalap definíciója szerint az alábbiaknak mindig igaznak kell lenniük, tekintettel az alábbi adatokra:

  1. A hosszúságnak mindig meg kell egyeznie a metódus bemenetének hosszával, setLength
  2. A szélességnek mindig meg kell egyeznie a metódus, a setBreadth bemeneteként átadott szélességgel
  3. A területnek mindig meg kell egyeznie a hosszúság és a szélesség szorzatával

Abban az esetben, ha megpróbáljuk létrehozni az ISA kapcsolatot a Négyzet és a Téglalap között, úgynevezett „Négyzet téglalap”, akkor a fenti kód váratlanul viselkedni kezdene, ha a Négyzet példányát elhaladják. A terület és az ellenőrzés ellenőrzése esetén állítólagos hiba lép fel szélességű, bár a program leáll, mivel az érvényesítési hiba a területellenőrzés sikertelensége miatt felmerül.

A Square osztálynak nincs szüksége olyan módszerekre, mint a setBreadth vagy a setLength. Az LSPDemo osztálynak ismernie kell a téglalap származtatott osztályainak részleteit (például négyzet), hogy megfelelően kódolhassa a dobási hibák elkerülése érdekében. A meglévő kód megváltoztatása elsősorban a nyitott-zárt elvet töri meg.

Interfész elkülönítési elve

Robert C. Martin leírja, hogy az ügyfeleket nem szabad arra kényszeríteni, hogy felesleges módszereket alkalmazzanak, amelyeket nem fognak használni.

AlapjánInterfész elkülönítési elvkliens, függetlenül attól, hogy mi az, amit soha nem szabad arra kényszeríteni, hogy olyan interfészt valósítson meg, amelyet nem használ, vagy az ügyfelet soha nem kell köteleznie semmilyen módszerre, amelyet nem ők használnak. Tehát alapvetően az interfész szegregációjának alapelvei csatolófelületek, amelyek kicsiek, de kliensspecifikusak, monolitikus és nagyobb felület helyett. Röviden: rossz lenne, ha rákényszerítené az ügyfelet, hogy függjen egy bizonyos dologtól, amire nincs szüksége.

Például a naplók írásához és olvasásához egyetlen naplózási felület hasznos egy adatbázis számára, de a konzol számára nem. A naplók olvasásának nincs értelme a konzol naplózójának. Folytatás ezzel a SZOLID alapelvekkel a Java cikkben.

Miért van szükség erre az elvre?

Tegyük fel, hogy van egy éttermi felület, amely módszereket tartalmaz az online ügyfelek, a betárcsázós, telefonos és a bejáró ügyfelek megrendelésének elfogadására. Emellett tartalmaz online fizetési módokat (online ügyfelek számára) és személyes fizetéseket (bejáró ügyfelek, valamint telefonos ügyfelek számára, amikor megrendelésüket otthon kézbesítik).

Most hozzunk létre egy Java felületet az étterem számára, és nevezzük el RestaurantInterface.java néven.

nyilvános felület RestaurantInterface {public void acceptOnlineOrder () public void takeTelephoneOrder () public void payOnline () public void walkInCustomerOrder () public void payInPerson ()}

A RestaurantInterface-ben 5 módszer van meghatározva, amelyek online megrendelés elfogadására, telefonos megrendelés elfogadására, megrendelések elfogadására egy bejáratott ügyféltől, online fizetés elfogadására és személyesen történő fizetés elfogadására vonatkoznak.

Kezdjük azzal, hogy az online ügyfelek számára a RestaurantInterface-t OnlineClientImpl.java néven hajtjuk végre

public class OnlineClientImpl megvalósítja a RestaurantInterface {public void acceptOnlineOrder () {// logika az online megrendelés leadásához} public void takeTelephoneOrder () {// Nem alkalmazható az online rendeléshez dobja az új UnsupportedOperationException ()} public void payOnline () {// fizetési logikát online} public void walkInCustomerOrder () {// Nem alkalmazható az online rendelésnél dobja az új UnsupportedOperationException ()} public void payInPerson () {// Nem alkalmazható az online rendeléshez dobja az új UnsupportedOperationException ()}}
  • Mivel a fenti kód (OnlineClientImpl.java) az online megrendelésekre vonatkozik, dobja az UnsupportedOperationException parancsot.

  • Az online, telefonos és bejárható ügyfelek mindegyikükre alkalmazzák a RestaurantInterface megvalósítást.

  • A Telephonic kliens és a Walk-in kliens megvalósítási osztályai nem támogatott módszerekkel rendelkeznek.

    sql és pl sql oktatóanyag
  • Mivel az 5 módszer a RestaurantInterface része, a megvalósítási osztályoknak mind az 5-öt végre kell hajtaniuk.

  • Az egyes implementációs osztályok által az UnsupportedOperationException dobott módszerek. Amint jól látható - az összes módszer megvalósítása nem hatékony.

  • A RestaurantInterface bármelyik módszerének bármilyen változása átkerül az összes megvalósítási osztályba. Ekkor a kód karbantartása valóban nehézkessé válik, és a változások regressziós hatásai folyamatosan növekedni fognak.

  • Az RestaurantInterface.java megsérti az egyfelelősség elvét, mert a fizetések logikája, valamint a megrendelések leadásának logikája egyetlen felületen van csoportosítva.

A fenti problémák kiküszöbölésére az interfész szegregációs elvét alkalmazzuk a fenti terv átalakítására.

  1. Válassza szét a fizetési és a rendelési feladatok funkcióit két különálló felületre, a PaymentInterface.java és a OrderInterface.java.

  2. Mindegyik ügyfél a PaymentInterface és a OrderInterface egy-egy megvalósítását használja. Például - az OnlineClient.java az OnlinePaymentImpl és az OnlineOrderImpl stb.

  3. Az Egységes Felelősség Elve csatolva van, mint fizetési felület (PaymentInterface.java) és rendelési felület (OrderInterface).

  4. Az egyik megbízási vagy fizetési felület változása nem érinti a másikat. Most függetlenek. Nem lesz szükség semmilyen dummy végrehajtásra vagy egy UnsupportedOperationException dobására, mivel minden felület csak olyan módszerekkel rendelkezik, amelyeket mindig használni fog.

Az ISP alkalmazása után

Függőségi inverzió elve

Robert C. Martin leírja, mivel az absztrakcióktól függ, nem pedig a konkretizációktól. Eszerint a magas szintű modul soha nem támaszkodhat egyetlen alacsony szintű modulra sem. például

Elmegy egy helyi boltba vásárolni valamit, és úgy dönt, hogy fizet a betéti kártya használatával. Tehát, amikor átadja a kártyáját az ügyintézőnek a fizetés elvégzéséhez, az ügyintéző nem veszi észre, hogy ellenőrizze, milyen kártyát adott.

Még akkor is, ha Ön Visa-kártyát adott, ő nem tesz ki Visa-automatát a kártya ellopása miatt. A fizetéshez használt hitelkártya vagy betéti kártya típusa nem is számít, egyszerűen ellopják. Tehát ebben a példában láthatja, hogy mind Ön, mind az ügyintéző függ a hitelkártya-elvonástól, és nem aggódik a kártya sajátosságai miatt. Ez a függőségi inverzió elve.

Miért van szükség erre az elvre?

Lehetővé teszi a programozó számára a keményen kódolt függőségek eltávolítását, hogy az alkalmazás lazán összekapcsolódjon és kiterjeszthető legyen.

public class Student {privát cím címe public Student () {address = új cím ()}}

A fenti példában a Student osztálynak szüksége van egy Cím objektumra, és ő felel a Cím objektum inicializálásáért és használatáért. Ha a jövőben megváltozik a Cím osztály, akkor a Diák osztályban is változtatnunk kell. Ez szorosan összekapcsolja a Student és a Address objektumokat. Megoldhatjuk ezt a problémát a függőségi inverzió tervezési mintájával. azaz a címobjektum önállóan valósul meg, és akkor kapja meg a hallgató számára, amikor a konstruktort vagy szetter alapú függőségi inverziót használva a Student példányos.

Ezzel a SAVID Principek végére értünk a Java-ban.

Nézze meg a az Edureka, egy megbízható online tanulási vállalat, amelynek több mint 250 000 elégedett tanulóval rendelkező hálózata elterjedt az egész világon. Az Edureka Java J2EE és SOA képzési és tanúsítási tanfolyamát olyan hallgatók és szakemberek számára tervezték, akik Java fejlesztők szeretnének lenni. A tanfolyamot úgy tervezték, hogy előrelépést nyújtson a Java programozásban, és kiképezzen mind az alapvető, mind a fejlett Java koncepciókra, valamint a különböző Java keretrendszerekkel, például a Hibernate & Spring.

Van egy kérdésünk? Kérjük, említse meg a „SZOLID alapelvek a Java-ban” blog megjegyzés rovatában, és a lehető leghamarabb kapcsolatba lépünk Önnel.