Artikuj

E hapur / e mbyllur, sipas parimit SOLID

Subjektet e softuerit (klasat, modulet, funksionet, etj.) Duhet të jenë të hapura për zgjerim, por të mbyllura për redaktim.

Dizajnimi i softuerit: module, klasa dhe funksione në mënyrë të tillë që kur të jetë e nevojshme funksionaliteti i ri, ne nuk duhet të modifikojmë kodin ekzistues por të shkruajmë kod të ri që do të përdoret nga kodi ekzistues. Kjo mund të tingëllojë e çuditshme, veçanërisht me gjuhë si Java, C, C ++ ose C # ku vlen jo vetëm për vetë kodin burimor por edhe për binarin. Ne duam të krijojmë karakteristika të reja në mënyra që nuk kërkojnë rishpërndarjen e binareve ekzistuese, ekzekutuesve ose DLL-ve.
OCP në kontekstin SOLID

 

SRP dhe OCP plotësuese

Ne tashmë kemi parë parimin e PSR-së të Përgjegjësisë së Thjeshtë i cili thotë se një modul duhet të ketë vetëm një arsye për të ndryshuar. Parimet e OCP dhe SRP janë plotësuese. Kodi i hartuar duke ndjekur parimin SRP do të respektojë gjithashtu parimet e OCP. Kur kemi një kod që ka vetëm një arsye për të ndryshuar, prezantimi i një veçorie të re do të krijojë një arsye dytësore për atë ndryshim. Pra, të dy SRP dhe OCP do të shkelen. Po kështu, nëse kemi një kod që duhet të ndryshojë vetëm kur ndryshon funksioni i tij kryesor dhe duhet të mbetet i pandryshuar kur shtohet funksionaliteti i ri, duke respektuar kështu OCP, ai gjithashtu kryesisht do të respektojë SRP.
Kjo nuk do të thotë që SRP gjithmonë të çon në OCP ose anasjelltas, por në shumicën e rasteve nëse respektohet njëra prej tyre, arritja e së dytës është mjaft e thjeshtë.

 

Shembull i shkeljes së parimit të OCP

Nga një këndvështrim thjesht teknik, Parimi i Hapur / Mbyllur është shumë i thjeshtë. Një marrëdhënie e thjeshtë midis dy klasave, si ajo më poshtë, shkel parimin e OCP.

Klasa User përdor direkt klasën Logic. Nëse duhet të implementojmë një klasë të dytë Logic në një mënyrë që të na lejojë të përdorim edhe atë aktuale edhe atë të re, klasa ekzistuese Logic do të duhet të ndryshohet. Përdoruesi është i lidhur drejtpërdrejt me zbatimin e logjikës, nuk ka asnjë mënyrë që ne të sigurojmë logjikë të re pa ndikuar në atë aktuale. Dhe kur flasim për gjuhë të shtypura në mënyrë statike, klasa e Përdoruesit ka shumë të ngjarë të kërkojë ndryshime gjithashtu. Nëse flasim për gjuhë të përpiluara, me siguri edhe ekzekutuesja e Përdoruesit dhe ajo ekzekutuese Logic ose biblioteka dinamike do të kërkojë rikompilimin dhe dorëzimin, preferohet të shmanget kur është e mundur.

Duke iu referuar skemës së mëparshme, ne mund të konkludojmë se çdo klasë që përdor drejtpërdrejt një klasë tjetër, mund të çojë në shkeljen e parimit Hapur / Mbyllur. 
Le të supozojmë se duam të shkruajmë një klasë të aftë të sigurojë përparimin "në përqindje" të një skedari të shkarkuar, përmes aplikacionit tonë. Ne do të kemi dy klasa kryesore, një Progres dhe një Skedar, dhe mendoj se do të dëshironim t'i përdorim ato si më poshtë:

 

funksioni testItCanGetTheProgressOfAFileAsAPercent () {
     $ skedar = Skedar i ri ();
     $ skedar-> gjatësia = 200;
     $ skedar-> i dërguar = 100;
     $ progress = Progres i ri (skedari $);
     $ this-> assertEquals (50, $ progress-> getAsPercent ());
}

Në këtë kod jemi përdorues të Progresit. Ne duam të marrim një vlerë si përqindje, pavarësisht nga madhësia aktuale e skedarit. Ne përdorim File si një burim informacioni. Një skedar ka një gjatësi në bajte dhe një fushë të quajtur dërguar që përfaqëson sasinë e të dhënave të dërguara tek shkarkuesi. Ne nuk na intereson se si azhurnohen këto vlera në aplikacion. Ne mund të supozojmë se ka ndonjë logjikë magjike që e bën këtë për ne, kështu që në një provë ne mund t'i vendosim në mënyrë të qartë ato.

 

Dosja e klasës {
     gjatësia publike $;
     publik $ dërguar;
}

 

Klasa File është vetëm një objekt i thjeshtë i të dhënave që përmban të dy fushat. Sigurisht që duhet të përmbajë gjithashtu informacione dhe sjellje të tjera, të tilla si emri i skedarit, rruga, rruga relative, drejtoria aktuale, lloji, lejet, etj.

 

Progresi i klasës {

     skedar privat $;

     funksioni __construktoni (Skedari $ skedar) {
          $ kjo-> skedari = $ skedari;
     }

     funksioni getAsPercent () {
          ktheje $ kjo-> skedar-> dërguar * 100 / $ kjo-> skedar-> gjatësi;
     }

}

Progresi është thjesht një klasë që pranon një skedar në konstruktorin e saj. Për qartësi, ne kemi specifikuar llojin e ndryshores në parametrat e konstruktorit. Ekziston një metodë e vetme e dobishme në Progress, getAsPercent (), e cila do të marrë vlerat dhe gjatësinë e dërguar nga File dhe do t'i kthejë ato në përqindje. E thjeshtë dhe funksionon.

Ky kod duket të jetë i saktë, megjithatë shkel parimin e Hapur / Mbyllur.

Por pse?

Dhe si?

 

Le të përpiqemi të ndryshojmë kërkesat

Çdo aplikacion për t'u zhvilluar me kalimin e kohës do të ketë nevojë për veçori të reja. Një tipar i ri për aplikacionin tonë mund të jetë lejimi i transmetimit të muzikës në vend të shkarkimit të skedarëve. Gjatësia e skedarit përfaqësohet në bajte, kohëzgjatja e muzikës në sekonda. Ne duam t'u ofrojmë dëgjuesve tanë një shirit progresi, por a mund ta ripërdorim klasën e shkruar më sipër?

Jo nuk mundemi. Përparimi ynë është i detyruar të File. Mund të menaxhojë vetëm informacionin e skedarit, megjithëse mund të zbatohet edhe në përmbajtjen muzikore. Por, për ta bërë këtë, duhet ta modifikojmë, duhet ta bëjmë Progresin të njohë muzikën dhe skedarët. Nëse dizajni ynë përputhet me OCP, nuk do të na duhet të prekim Skedarin ose Progresin. Thjesht mund të ripërdorim progresin ekzistues dhe ta zbatojmë atë në muzikë.

 

Buletini i inovacionit
Mos humbisni lajmet më të rëndësishme mbi inovacionin. Regjistrohuni për t'i marrë ato me email.

Zgjidhja e mundshme

Gjuhët e shtypura në mënyrë dinamike kanë avantazhin e menaxhimit të llojeve të objekteve në kohën e ekzekutimit. Kjo na lejon të heqim modelin nga konstruktori i Progresit dhe kodi do të vazhdojë të funksionojë.

Progresi i klasës {

     skedar privat $;

     funksioni __construktoni (skedari $) {
         $ kjo-> skedari = $ skedari;
     }

    funksioni getAsPercent () {
         ktheje $ kjo-> skedar-> dërguar * 100 / $ kjo-> skedar-> gjatësi;
     }

}

Tani mund të lëshojmë gjithçka në Progres. Dhe me ndonjë gjë, unë dua të them fjalë për fjalë çdo gjë:

klasa Muzika {

gjatësia publike $;
publik $ dërguar;

artist publik $;
album publik $;
publike $ releaseDate;

funksioni getAlbumCoverFile () {
ktheni 'Imazhe / Mbulesa /'. $ kjo-> artist. '/'. $ ky-> album. '.png';
}
}

Dhe klasa e Muzikës si ajo e mësipërme do të funksionojë në mënyrë perfekte. Ne mund ta provojmë lehtë me një test shumë të ngjashëm me File.
funksioni testItCanGetTheProgressOfAMusicStreamAsAPercent () {
$ muzikë = Muzikë e re ();
$ muzikë-> gjatësi = 200;
$ muzikë-> dërguar = 100;

$ progress = Progres i ri ($ muzikë);

$ this-> assertEquals (50, $ progress-> getAsPercent ());
}

Pra, në thelb çdo përmbajtje e matshme mund të përdoret me klasën Progress. Ndoshta duhet ta shprehim atë në kod duke ndryshuar edhe emrin e ndryshores:

Progresi i klasës {

$ Përmbajtja e matshme private;

funksioni __konstruktoni ($ përmbajtje e matshme) {
$ kjo-> përmbajtje e matshme = $ përmbajtje e matshme;
}

funksioni getAsPercent () {
kthe $ $-> Përmbajtja e matshme-> dërguar * 100 / $ kjo-> Përmbajtja e matshme-> gjatësia;
}

}

Kur specifikuam File si model, ne ishim optimistë për atë që klasa jonë mund të trajtojë. Ishte e qartë dhe nëse do të vinte diçka tjetër, një gabim i madh do të na tregonte.

Uklasa na që tejkalon një metodë të një klase bazë të tillë që kontrata e klasës bazë të mos respektohet nga klasa e prejardhur. 

Ne nuk duam të përfundojmë duke u përpjekur për të thirrur metoda ose fusha hyrjeje në objekte që nuk përputhen me kontratën tonë. Kur kishim një lloj shtypi, kontrata specifikohej prej saj. Fushat dhe metodat e klasës File. Tani që nuk kemi asgjë, ne mund të dërgojmë ndonjë gjë, madje edhe një varg dhe kjo do të rezultojë në një gabim të keq.

Ndërsa rezultati përfundimtar është i njëjtë në të dyja rastet, që do të thotë se kodi prishet, i pari prodhoi një mesazh të këndshëm. Megjithatë, kjo është shumë e errët. Nuk ka asnjë mënyrë për të ditur se çfarë është ndryshorja - një varg në rastin tonë - dhe cilat prona u kërkuan dhe nuk u gjetën. Është e vështirë për të korrigjuar dhe rregulluar problemin. Një programues duhet të hapë klasën Progress, ta lexojë dhe ta kuptojë atë. Kontrata, në këtë rast, kur nuk e specifikoni në mënyrë eksplicite tiparin, është defindikuar nga sjellja e Përparimit. Është një kontratë e nënkuptuar e njohur vetëm për Progresin. Në shembullin tonë, është defikryhet duke hyrë në dy fushat, dërguar dhe gjatësi, në metodën getAsPercent(). Në jetën reale kontrata e nënkuptuar mund të jetë shumë komplekse dhe e vështirë për t'u zbuluar vetëm duke kërkuar për disa sekonda në klasë.

Kjo zgjidhje rekomandohet vetëm nëse asnjë nga sugjerimet e tjera më poshtë nuk mund të zbatohet lehtësisht ose nëse ato do të shkaktonin ndryshime serioze arkitektonike që nuk kërkojnë përpjekje.

Ercole Palmeri

Buletini i inovacionit
Mos humbisni lajmet më të rëndësishme mbi inovacionin. Regjistrohuni për t'i marrë ato me email.

Artikujt e fundit

Ndërhyrje novatore në realitetin e shtuar, me një shikues Apple në Poliklinikën Catania

Një operacion oftalmoplastik duke përdorur shikuesin komercial Apple Vision Pro u krye në Poliklinikën Catania…

3 Maj 2024

Përfitimet e Faqeve të Ngjyrosjes për Fëmijë - një botë magjike për të gjitha moshat

Zhvillimi i aftësive të shkëlqyera motorike përmes ngjyrosjes i përgatit fëmijët për aftësi më komplekse si shkrimi. Për të ngjyrosur…

2 Maj 2024

E ardhmja është këtu: Si industria e transportit po revolucionarizon ekonominë globale

Sektori detar është një fuqi e vërtetë ekonomike globale, e cila ka lundruar drejt një tregu prej 150 miliardë...

1 Maj 2024

Botuesit dhe OpenAI nënshkruajnë marrëveshje për të rregulluar rrjedhën e informacionit të përpunuar nga Inteligjenca Artificiale

Të hënën e kaluar, Financial Times njoftoi një marrëveshje me OpenAI. FT licencon gazetarinë e saj të klasit botëror…

30 Prill 2024