portaldacalheta.pt
  • Põhiline
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
Tehnoloogia

Hoidke raamistikku - sõltuvuse süstimismustrite uurimine



Traditsioonilised vaated juhtimise inversioonil (IoC) näib tõmbavat karmi joone kahe erineva lähenemisviisi vahel: teenuse lokaatori ja sõltuvuse süstimise (DI) mustrid.

Praktiliselt iga projekt, mida tean, sisaldab DI-raamistikku. Inimesed tõmbuvad nende poole, kuna nad soodustavad klientide ja nende sõltuvuste vaba sidumist (tavaliselt konstruktori sissepritsimise kaudu) minimaalse katlakoodi koodiga või üldse mitte. Ehkki see sobib suurepäraseks kiireks arenguks, leiavad mõned inimesed, et see võib muuta koodi jälgimise ja silumise raskeks. „Maagiline telgitagused“ saavutatakse tavaliselt refleksiooni kaudu, mis võib tuua terve hulga uusi probleeme.



Selles artiklis uurime alternatiivset mustrit, mis sobib hästi Java 8+ ja Kotlini koodibaasidele. See säilitab suurema osa DI-raamistiku eelistest, olles samas sama otsene kui teenuseotsija, ilma et oleks vaja väliseid tööriistu.



Motivatsioon

  • Vältige väliseid sõltuvusi
  • Vältige peegeldust
  • Reklaamige konstruktori süstimist
  • Minimeerige käitamise käitumine

Näide

Järgmises näites modelleerime teleri rakendust, kus sisu saamiseks võib kasutada erinevaid allikaid. Peame ehitama seadme, mis suudaks vastu võtta signaale erinevatest allikatest (nt maapealsed, kaabel-, satelliit- jms). Ehitame järgmise klasside hierarhia:



Suvalise signaaliallika rakendava teleriseadme klassihierarhia

Alustame nüüd traditsioonilisest DI-rakendusest, kus selline raamistik nagu Spring on meie jaoks kõik juhtmestik:



public class TV { private final TvSource source; public TV(TvSource source) { this.source = source; } public void turnOn() { System.out.println('Turning on the TV'); this.source.tuneChannel(42); } } public interface TvSource { void tuneChannel(int channel); } public class Terrestrial implements TvSource { @Override public void tuneChannel(int channel) { System.out.printf('Adjusting dish frequency to channel %d ', channel); } } public class Cable implements TvSource { @Override public void tuneChannel(int channel) { System.out.printf('Changing digital signal to channel %d ', channel); } }

Me märkame mõningaid asju:

  • Teleklass väljendab sõltuvust TvSource'ist. Väline raamistik näeb seda ja süstib konkreetse rakenduse (maapealne või kaabel).
  • Konstruktori sissepritse muster võimaldab hõlpsat katsetamist, kuna saate telerieksperte hõlpsasti üles ehitada alternatiivsete rakendustega.

Meil on hea algus, kuid mõistame, et selleks võib DI-raamistiku toomine olla veidi üle jõu. Mõned arendajad on teatanud probleemidest, mis on seotud ehitusprobleemide silumisega (pikad korstnajäljed, jälgimatud sõltuvused). Samuti on meie klient väljendanud, et tootmise ajad on oodatust veidi pikemad ning meie profiiliprofiil näitab peegeldavate kõnede aeglustumist.



Alternatiiviks oleks teenusepakkuja mustri rakendamine. See on lihtne, ei kasuta peegeldust ja võib olla meie väikese koodibaasi jaoks piisav. Teine võimalus on jätta klassid rahule ja kirjutada nende ümber sõltuvuse asukohakood.

Pärast paljude alternatiivide hindamist otsustame selle rakendada pakkujate liideste hierarhiana. Igal sõltuvusel on seotud pakkuja, kes vastutab ainuisikuliselt klassi sõltuvuste leidmise ja süstitud eksemplari loomise eest. Samuti muudame teenuseosutaja kasutamise hõlbustamiseks sisemise liidese. Nimetame seda Mixini süstimiseks, kuna iga pakkuja on oma sõltuvuste leidmiseks segatud teiste pakkujatega.



Üksikasjad selle kohta, miks ma selle struktuuriga otsustasin, on täpsustatud üksikasjades ja põhjendustes, kuid siin on lühike versioon:

  • See eraldab sõltuvuse asukoha käitumise.
  • Liideste laiendamine ei kuulu teemandiprobleemi.
  • Liidestel on vaikimisi rakendused.
  • Puuduvad sõltuvused takistavad kompileerimist (boonuspunktid!).

Järgmine diagramm näitab, kuidas sõltuvused ja teenusepakkujad suhtlevad, ja rakendamist on illustreeritud allpool. Lisame ka peamise meetodi, kuidas näidata oma sõltuvuste komponeerimist ja teleobjekti konstrueerimist. Selle näite leiate ka selle näite pikema versiooni GitHub .



Koostöö teenusepakkujate ja sõltuvuste vahel

public interface TvSource { void tuneChannel(int channel); interface Provider { TvSource tvSource(); } } public class TV { private final TvSource source; public TV(TvSource source) { this.source = source; } public void turnOn() { System.out.println('Turning on the TV'); this.source.tuneChannel(42); } interface Provider extends TvSource.Provider { default TV tv() { return new TV(tvSource()); } } } public class Terrestrial implements TvSource { @Override public void tuneChannel(int channel) { System.out.printf('Adjusting dish frequency to channel %d ', channel); } interface Provider extends TvSource.Provider { @Override default TvSource tvSource() { return new Terrestrial(); } } } public class Cable implements TvSource { @Override public void tuneChannel(int channel) { System.out.printf('Changing digital signal to channel %d ', channel); } interface Provider extends TvSource.Provider { @Override default TvSource tvSource() { return new Cable(); } } } // Here compose the code above to instantiate a TV with a Cable TvSource public class Main { public static void main(String[] args) { new MainContext().tv().turnOn(); } static class MainContext implements TV.Provider, Cable.Provider { } }

Mõned näited selle näite kohta:



  • Teleklass sõltub TvSource'ist, kuid see ei tea rakendamist.
  • TV.Provider laiendab TvSource.Providerit, kuna see vajab TvSource'i loomiseks meetodit tvSource () ja saab seda kasutada ka siis, kui seda seal pole rakendatud.
  • Teler saab maapealseid ja kaabliallikaid omavahel vahetada.
  • Terrestrial.Provider ja Cable.Provider liidesed pakuvad konkreetseid TvSource'i rakendusi.
  • Põhimeetodil on konkreetne rakendus MainContext of TV.Provider, mida kasutatakse TV-eksemplari hankimiseks.
  • Programm nõuab teleri kiirendamiseks TvSource.Provideri juurutamist kompileerimise ajal, seega lisame näiteks Cable.Provideri.

Üksikasjad ja põhjendus

Oleme näinud mustrit toimimas ja mõningaid selle taga olevaid põhjendusi. Te ei pruugi olla kindel, et peaksite seda juba praegu kasutama, ja teil oleks õigus; see pole just hõbekuul. Isiklikult usun, et see on enamikus aspektides parem teenuseotsija mustrist. Kuid võrreldes DI-raamistikega tuleb hinnata, kas eelised kaaluvad üles katlakoodi lisamise üldkulud.

Pakkujad laiendavad teisi teenusepakkujaid, et leida nende sõltuvus

Kui pakkuja laiendab teist, on sõltuvused seotud. See loob staatilise valideerimise põhialuse, mis takistab kehtetute kontekstide loomist.

Teenuse asukoha määramise mustri üks peamisi valupunkte on see, et peate helistama üldisele GetService() meetod, mis kuidagi teie sõltuvuse lahendab. Kompileerimise ajal ei ole teil mingeid garantiisid, et sõltuvus registreeritakse kunagi lokaatoris ja teie programm võib töötamise ajal ebaõnnestuda.

Ka DI muster ei lahenda seda. Sõltuvuse lahendamine toimub tavaliselt peegelduse kaudu välise tööriista abil, mis on enamasti kasutaja eest varjatud. See ebaõnnestub ka käitamise ajal, kui sõltuvused pole täidetud. Tööriistad nagu IntelliJ CDI (saadaval ainult tasulises versioonis) pakub mingil tasemel staatilist kinnitust, kuid ainult Pistoda näib, et eeltöötleja oma märkustega tegeleb selle probleemiga kujunduse järgi.

Klassid säilitavad DI-mudeli tüüpilise konstruktori süstimise

Arendajate kogukond seda ei nõua, kuid kindlasti soovib. Ühelt poolt saate lihtsalt vaadata konstruktorit ja kohe näha klassi sõltuvusi. Teiselt poolt võimaldab üksuse testimise liik millest paljud inimesed kinni peavad, st konstrueerides testitava subjekti tema sõltuvuste mõnitustega.

See ei tähenda, et muid mustreid ei toetata. Tegelikult võib isegi leida, et Mixin Injection lihtsustab testimiseks keerukate sõltuvusgraafikute koostamist, kuna peate juurutama ainult kontekstiklassi, mis laiendab teie subjekti pakkujat. MainContext ülaltoodud on suurepärane näide, kus kõigil liidestel on vaikerakendused, nii et sellel võib olla tühi rakendus. Sõltuvuse asendamine nõuab ainult selle pakkuja meetodi tühistamist.

Vaatame järgmist teleklasside testi. See peab teleri kiirendama, kuid klassi konstruktorile helistamise asemel kasutab telerit. Provideri liides. TvSource.Provideril pole vaikerakendust, seega peame selle ise kirjutama.

public class TVTest { @Test public void testWithProvider() { TvSource source = Mockito.mock(TvSource.class); TV.Provider provider = () -> source; // lambdas FTW provider.tv().turnOn(); Mockito.verify(source, times(1)).tuneChannel(42); } }

Lisame nüüd teleklassile veel ühe sõltuvuse. CathodeRayTube sõltuvus töötab võluväel, et pilt teleriekraanile ilmuks. See on teleri rakendusest lahti ühendatud, kuna võiksime tulevikus minna üle LCD-le või LED-ile.

public class TV { public TV(TvSource source, CathodeRayTube cathodeRayTube) { ... } public interface Provider extends TvSource.Provider, CathodeRayTube.Provider { default TV tv() { return new TV(tvSource(), cathodeRayTube()); } } } public class CathodeRayTube { public void beam() { System.out.println('Beaming electrons to produce the TV image'); } public interface Provider { default CathodeRayTube cathodeRayTube() { return new CathodeRayTube(); } } }

Kui teete seda, märkate, et äsja kirjutatud test koostab ja läbib ootuspäraselt. Lisasime telerisse uue sõltuvuse, kuid pakkusime ka vaikimisi rakendust. See tähendab, et me ei pea seda mõnitama, kui tahame lihtsalt kasutada reaalset teostust, ja meie testid võivad luua keerukaid objekte mis tahes soovitud visuaalse detailsusega.

See on kasulik, kui soovite keerulises klassihierarhias mõnd konkreetset pilgata (nt ainult andmebaasi juurdepääsukiht). Muster võimaldab sellist tüüpi hõlpsalt seadistada seltskondlikud testid mida mõnikord eelistatakse üksikkatsetele.

Sõltumata eelistusest võite olla kindel, et võite pöörduda mis tahes vormis testimise poole, mis sobib igas olukorras paremini teie vajadustele.

Vältige väliseid sõltuvusi

Nagu näete, puuduvad viited ega mainimised välistele komponentidele. See on paljude projektide jaoks võtmetähtsusega, millel on suurust või isegi turvanõuded. See aitab ka koostalitlusvõimet, sest raamistikud ei pea pühenduma konkreetsele DI-raamistikule. Java-s on tehtud jõupingutusi nagu JSR-330 sõltuvuse süstimine Java-standardile mis leevendavad ühilduvusprobleeme.

Vältige peegeldust

Teenuse lokaliseerimise rakendused ei tugine tavaliselt peegeldusele, kuid DI rakendused siiski tuginevad (välja arvatud märkimisväärne Dagger 2). Sellel on rakenduse käivitamise aeglustamise peamised puudused, kuna raamistik peab teie moodulid skannima, sõltuvusgraafiku lahendama, objektid peegeldavalt üles ehitama jne.

Mixin Injection nõuab, et kirjutaksite oma teenuste kiirendamiseks koodi, mis on sarnane teenuse asukoha mustris registreerimise etapiga. See väike lisatöö eemaldab peegeldavad kõned täielikult, muutes teie koodi kiiremaks ja arusaadavaks.

Kaks projekti, mis hiljuti minu tähelepanu pälvisid ja millest on kasu peegeldumise vältimisest, on Graali substraat VM ja Kotlin / Pärismaalane . Mõlemad kompileerivad algse baitkoodini ja see nõuab, et kompilaator teaks eelnevalt teie peegeldavatest kõnedest. Graali puhul on see täpsustatud punktis a JSON-fail, mida on raske kirjutada , ei saa staatiliselt kontrollida, seda ei saa oma lemmiktööriistade abil hõlpsasti taastada. Mixin Injectioni kasutamine peegeldumise vältimiseks on suurepärane võimalus saada omakeelse kompileerimise eeliseid.

Minimeerige käitamise käitumine

Nõutavate liideste juurutamise ja laiendamise abil koostate sõltuvusgraafiku üks tükk korraga. Iga pakkuja istub konkreetse rakenduse kõrval, mis toob teie programmi juurde korra ja loogika. Selline kiht on tuttav, kui olete varem kasutanud mustri Mixin või kooki.

Siinkohal tasub ehk rääkida klassist MainContext. See on sõltuvusgraafiku juur ja teab suurt pilti. See klass hõlmab kõiki teenusepakkujate liideseid ja on staatilise kontrolli lubamise võti. Kui naaseme näite juurde ja eemaldame Cable.Provideri rakenduste loendist, näeme seda selgelt:

kujundusdokumendi mall tarkvara arendamiseks
static class MainContext implements TV.Provider { } // ^^^ // MainContext is not abstract and does not override abstract method tvSource() in TvSource.Provider

Siin juhtus see, et rakendus ei määranud konkreetset kasutatavat TvSource'i ja kompilaator tabas vea. Teenuseotsija ja peegelpõhise DI-ga oleks see viga võinud jääda tähelepanuta, kuni programm tööajal kokku jooksis - isegi kui kõik üksuse testid olid läbitud! Usun, et need ja muud meie näidatud eelised kaaluvad üles mustri toimimiseks vajaliku katlakivi kirjutamise negatiivse külje.

Püüa ümmargused sõltuvused

Naaseme CathodeRayTube'i näite juurde ja lisame ringikujulise sõltuvuse. Oletame, et me tahame, et talle süstitakse telerieksemplari, seega laiendame telerit. Pakkuja:

public class CathodeRayTube { public interface Provider extends TV.Provider { // ^^^ // cyclic inheritance involving CathodeRayTube.Provider default CathodeRayTube cathodeRayTube() { return new CathodeRayTube(); } } }

Koostaja ei luba tsüklilist pärimist ja me ei saa sellist suhet määratleda. Enamik raamistikke ebaõnnestub käitamise ajal, kui see juhtub, ja arendajad kipuvad selle ümber töötama, et ainult programm käivitada. Kuigi seda anti-mustrit võib leida reaalses maailmas, on see tavaliselt märk halbast kujundusest. Kui koodi ei õnnestu koostada, peaksime julgustama otsima paremaid lahendusi, enne kui muutmiseks on liiga hilja.

Säilitage objektide ehitamisel lihtsus

Üks argument SL-i kasuks DI asemel on see, et seda on lihtne ja silumine lihtsam. Näidetest on selge, et sõltuvuse tuvastamine on lihtsalt pakkuja meetodi kõnede ahel. Sõltuvuse allika jälitamine on sama lihtne kui meetodikõnesse astumine ja vaatamine, kuhu jõuate. Silumine on mõlemast alternatiivist lihtsam, sest saate otse teenusepakkujalt navigeerida täpselt seal, kus sõltuvused on tuvastatud.

Teenuse eluiga

Tähelepanelik lugeja võib olla märganud, et see rakendus ei lahenda teenuse eluea probleemi. Kõik pakkujate pakutavad meetodid kutsuvad esile uued objektid, muutes selle sarnaseks Kevade prototüübi ulatus .

See ja muud kaalutlused jäävad selle artikli reguleerimisalast veidi välja, kuna tahtsin lihtsalt tutvustada mustri olemust üksikasju häirimata. Toote täielikul kasutamisel ja rakendamisel tuleks siiski arvestada täislahendusega koos eluaegse toega.

Järeldus

Sõltumata sellest, kas olete harjunud sõltuvuse süstimise raamistikega või kirjutate oma teenuse lokaatorid, võiksite seda alternatiivi uurida. Kaaluge mixin-mustri kasutamist, mida me just nägime, ja vaadake, kas saate oma koodi turvalisemaks ja mõistlikumaks muuta.

Seotud: JS-i parimad tavad: ehitage Discript Bot koos TypeScripti ja sõltuvuse süstimisega

Põhitõdesid mõistes

Mida mõeldakse kontrolli inversiooni all?

Räägime juhtimise inversioonist, kui koodijupp on võimeline delegeerima käitumise teisele komponendile, mis pole selle koodi kirjutamise ajal teada (tavaliselt üldtuntud liideste ja laienduspunktide kaudu).

Mis on sõltuvuse süstimine?

Sõltuvuse süstimine on muster, mille abil saame saavutada süsteemi komponentide tasemel juhtimise inversiooni. Klass deklareerib ainult eesmärgi saavutamiseks vajalikke komponente, mitte seda, kuidas neid komponente leida või luua.

Põhjalik juhend JavaScripti kujundusmustrite kohta

Tagumine Ots

Põhjalik juhend JavaScripti kujundusmustrite kohta
Võistluslik masinõpe: kuidas rünnata ja kaitsta ML-mudeleid

Võistluslik masinõpe: kuidas rünnata ja kaitsta ML-mudeleid

Andmeteadus Ja Andmebaasid

Lemmik Postitused
Äriplaani anatoomia
Äriplaani anatoomia
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Ladina-Ameerika ühinemiste ja ühinemiste parimad tavad
Tarkvara kulude hindamine agiilses projektijuhtimises
Tarkvara kulude hindamine agiilses projektijuhtimises
Andekus pole kaup
Andekus pole kaup
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
Veebi juurdepääsetavus: miks W3C standardeid sageli eiratakse
 
Bränding on surnud, CX Design on kuningas
Bränding on surnud, CX Design on kuningas
Chatbot UX - disaininõuanded ja kaalutlused
Chatbot UX - disaininõuanded ja kaalutlused
Uus ettevõtluslaine
Uus ettevõtluslaine
Optimeeritud järjestikune keskmine kvantimise teisendus
Optimeeritud järjestikune keskmine kvantimise teisendus
Kasutajauuringute väärtus
Kasutajauuringute väärtus
Lemmik Postitused
  • erinevus s corp c corp
  • 1099 vs w2 määra kalkulaator
  • angularjs rakenduse näidis koos mvc-ga
  • gestalt läheduse printsiip
  • php teisendada utf 8-ks
Kategooriad
  • Tooteinimesed Ja Meeskonnad
  • Ux Disain
  • Protsess Ja Tööriistad
  • Andmeteadus Ja Andmebaasid
  • © 2022 | Kõik Õigused Kaitstud

    portaldacalheta.pt