Ar priklausomybė turėtų būti susijusi su kapsulėmis?

Jei teisingai suprantu, tipinis injekcijos priklausomybės mechanizmas yra injekcija per klasės konstruktorių arba per klasės viešąjį turtą (narį).

Tai užtikrina įdėtą priklausomybę ir pažeidžia OOP kapsuliavimo principą.

Ar teisingai apibrėžiu šį kompromisą? Kaip tvarkote šią problemą?

Taip pat žr. Mano atsakymą į toliau pateiktą klausimą.

113
17 июня '09 в 9:58 2009-06-17 09:58 „Urig “ nustatoma birželio 17 d. 09 val. 09:58 2009-06-17 09:58
@ 20 atsakymų

Yra dar vienas būdas pažvelgti į šią problemą, kuri jums gali atrodyti įdomi.

Kai naudojame IoC / priklausomybės injekciją, nenaudojame OOP koncepcijų. Žinoma, mes naudojame OO kaip „šeimininką“, tačiau idėjos, esančios „IoC“, yra programinės įrangos kūrimas, kuris yra orientuotas į komponentus, o ne OO.

Komponentų programinė įranga skirta valdyti priklausomybę - pavyzdys yra bendras .NET surinkimo mechanizmas. Kiekviena asamblėja skelbia sąrałų, kuriuos ji nurodo, sąrašą, o tai supaprastina dalių, reikalingų programai pradėti, surinkimą (ir patikrinimą).

Naudodami panašius metodus mūsų OO programose per „IoC“, siekiame supaprastinti programų diegimą ir priežiūrą. Leidybos priklausomybės (pvz., Konstruktoriaus parametrai ar kažkas kita) yra pagrindinė šio proceso dalis. Kapsuliavimas iš tikrųjų netaikomas, kaip ir komponento / paslaugos orientuotame pasaulyje.

Deja, šiuo metu mūsų kalbos nesiskiria smulkiagrūdėmis, į objektus orientuotomis koncepcijomis iš rimtesnių komponentų, todėl tai yra skirtumas, kurį turėtumėte laikytis tik savo mintyse :)

57
26 нояб. Atsakymą pateikė Nicholas Blumhardt lapkričio 26 d 2009-11-26 03:11 '09 3:11 am 2009-11-26 03:11

Geras klausimas - bet tam tikru momentu jo grynoji forma turi būti sulaužyta, jei objektas kada nors atlieka savo priklausomybę. Kai kurie priklausomi paslaugų teikėjai turi žinoti, kad atitinkamas objektas reikalauja „ Foo , o paslaugų teikėjas turi turėti būdą suteikti „ Foo objektui.

Klasikiniu būdu, šis pastarasis atvejis yra tvarkomas, kaip sakote, naudojant konstruktoriaus argumentus arba setter metodus. Tačiau nebūtinai taip yra: aš žinau, kad, pvz., Naujausios „Spring DI“ sistemos versijos „Java“ leidžia jums pažymėti privačius laukus (pvz., Naudojant „ @Autowired ), o priklausomybė bus nustatyta per atspindį, nereikalaujant atskleisti priklausomybės per bet kurį viešieji metodai / konstruktorių klasės. Tai gali būti sprendimas, kurio ieškojote.

Tačiau nemanau, kad konstruktoriaus injekcija taip pat yra problema. Visada maniau, kad po statybų objektai turėtų būti visiškai funkcionalūs, kad viskas, ko jiems reikia jų vaidmeniui (ty būti teisingoje būsenoje), bet kokiu atveju turėtų būti teikiama per konstruktorių. Jei turite objektą, kuriam reikalingas bendro autoriaus darbas, man atrodo, kad dizaineris viešai skelbia šį reikalavimą ir garantuoja, kad jis bus įvykdytas, kai bus sukurtas naujas klasės egzempliorius.

Idealiu atveju, dirbant su objektais, vis dar sąveikaujate su jais per sąsają, ir kuo daugiau tai padarysite (ir turite priklausomybę, susijusią su DI), tuo mažiau turite susidoroti su dizaineriais. Idealiu atveju jūsų kodas nėra įtrauktas arba net nesukuria konkrečių klasių atvejų; todėl jis tiesiog priskiria IFoo per DI, nesijaudindamas, kad FooImpl konstruktorius nurodo, kad jis turi atlikti savo darbą, ir iš tikrųjų net nesuvokia FooImpl egzistavimo. Šiuo požiūriu kapsuliavimas yra tobulas.

Ši nuomonė, žinoma, yra, bet, mano nuomone, DI nebūtinai sulaužo kapsulę ir iš tikrųjų gali padėti jai sutelkti visas būtinas vidaus organų žinias vienoje vietoje. Tai ne tik savaime, bet dar geriau, kad ši vieta yra už jūsų kodo bazės ribų, todėl nė vienas iš jūsų parašytų kodų neturėtų žinoti apie klasės priklausomybes.

29
17 июня '09 в 14:17 2009-06-17 14:17 atsakymą pateikė Andrzej Doyle, birželio 17 d., 09:17, 2009-06-17 14:17

Tai užtikrina įdėtą priklausomybę ir pažeidžia OOP kapsuliavimo principą.

Na, kad būtų sąžiningas, viskas pertrauka į kapsulę. :) Tai yra švelnus principas, kurį reikia gerai elgtis.

Taigi, kas pertrauka į kapsulę?

Paveldima.

„Kadangi paveldėjimas suteikia subklasę detalėms apie jos tėvų įgyvendinimą, dažnai sakoma, kad„ paveldėjimo pertraukos yra kapsulės “(Gang of Four 1995: 19)

Su aspektais susijęs programavimas . Pvz., Registruojate „onMethodCall“ () atgalinį ryšį, ir tai suteikia jums puikią galimybę įvesti kodą į įprastą metodo įvertinimą, pridedant keistų šalutinių efektų ir pan.

Paskelbus draugą „C ++“, tai daroma .

Klasės pratęsimas rubinu veikia . Tiesiog iš naujo apibrėžkite eilutės metodą kažkur po visos eilutės apibrėžimo.

Na, daug dalykų.

Kapsuliavimas yra geras ir svarbus principas. Bet ne vienintelis.

 switch (principle) { case encapsulation: if (there_is_a_reason) break! } 
14
25 апр. atsakymas pateikiamas iki rugpjūčio 25 d. 2010-04-25 21:54 '10, 21:54, 2010-04-25 21:54

Taip, DI pertraukia kapsulę (dar vadinama „slėpimo informacija“).

Tačiau tikroji problema kyla tada, kai kūrėjai ją naudoja kaip pretekstą pažeidžiant „KISS“ („Keep It Short“ ir „Simple“) ir „YAGNI“ (jums neturėtų būti) principus.

Asmeniškai aš renkuosi paprastus ir efektyvius sprendimus. Iš esmės aš naudoju „naują“ operatorių, kad sukurtumėte valstybės priklausomybes, kada ir kur jos reikalingos. Tai paprasta, gerai supakuota, lengvai suprantama ir lengvai išbandoma. Tad kodėl gi ne?

11
22 июля '09 в 2:10 2009-07-22 02:10 atsakymą davė Rogério liepos 22 d., 02:10, 2009-07-22 02:10

Jis nepažeidžia kapsulių. Jūs suteikiate darbuotojui, tačiau klasė turi nuspręsti, kaip ji naudojama. Tol, kol sekate Pasakykite, neklauskite , viskas tvarkinga. Manau, kad dizainerio injekcija yra pageidautina, tačiau kūrėjai gali būti gražūs, bet dabar jie yra protingi. Tai reiškia, kad jose yra logika, skirta išlaikyti invariantus, kuriuos klasė atstovauja.

6
17 июня '09 в 10:57 2009-06-17 10:57 Atsakymą pateikė Jason Watkins birželio 17 d., 09:57, 2009-06-17 10:57

Geras adrenalino injekcijos indas / sistema suteiks dizainerio injekciją. Priklausomi objektai bus kapsuluojami ir neturėtų būti skelbiami viešai. Be to, padedant DP sistemai, nė vienas iš jūsų kodų netgi „nežino“ objekto statymo detalių, galbūt net įtraukiant sukurtą objektą. Tokiu atveju yra daugiau kapsulių, nes beveik visas jūsų kodas yra apsaugotas ne tik nuo kapsuluotų objektų žinių, bet net nedalyvauja objektų statyboje.

Dabar manau, kad jūs lyginate atvejį, kai sukurtas objektas sukuria savo kapsulinius objektus, greičiausiai, jo konstruktoriuje. Mano supratimas apie VB yra tai, kad norime, kad ši atsakomybė būtų pašalinta iš priemonės ir perduota kitam. Šiuo tikslu „kažkas kitas“, kuris šiuo atveju yra DP konteineris, gerai žino, kad „pertrauka“ kapsulę; privalumas yra tas, kad ji ištraukia šias žinias iš paties objekto. Kažkas turėtų tai turėti. Likusi jūsų paraiškos dalis neveikia.

Aš taip manau apie tai. Priklausomybės įpurškimo konteineris / sistema pažeidžia kapsulę, bet jūsų kodas nėra. Tiesą sakant, jūsų kodas yra „kapsulesnis“ nei bet kada.

5
17 июня '09 в 13:49 2009-06-17 13:49 atsakymas pateiktas Bill'e birželio 17 d., 09:49 2009-06-17 13:49

Tai panaši į pirmiau pateiktą atsakymą, bet noriu garsiai galvoti - galbūt kiti taip mato.

  • „Classic OO“ naudoja konstruktorius, kad nustatytų viešą „iniciacijos“ sutartį šios klasės vartotojams (slepia VISUS diegimo detales, taip pat užsandarinimą). Ši sutartis gali garantuoti, kad sukūrę egzempliorių, turite paruoštą naudoti objektą (t. Y. Papildomus inicializavimo veiksmus, kuriuos naudotojui reikia prisiminti (pamiršti, pamiršti)).

  • (konstruktorius) DI neabejotinai pažeidžia kraujavimo kapsulę per įgyvendinimo atvirkščiai, esančią už šios atviros dizainerio sąsajos. Nors vis dar svarstome viešą konstruktorių, atsakingą už naudotojų iniciacijos sutarties apibrėžimą, sukūrėme baisų kapsulių pažeidimą.

Teorinis pavyzdys:

Foo klasė turi 4 metodus ir reikalauja inicijuoti sveiką skaičių, todėl jo konstruktorius atrodo kaip „ Foo“ (int dydis) , o Foo klasės vartotojams iškart tampa aišku, kad jie turi pateikti dydį kurdami „Foo“ egzempliorių darbui.

Pasakykite, kad dėl šio konkretaus „Foo“ įgyvendinimo IWidget gali tekti atlikti savo darbą. Šio priklausomybės dizaino įpurškimas mums sukurtų konstruktorių, pvz., „ Foo“ (valdiklio dydis, „IWidget“ valdiklis)

Tai, kas mane erzina, dabar turime konstruktorių, kuris sujungia inicijavimo duomenis su priklausomybėmis - vienas įvestis domina klasės ( dydžio ) naudotoją, kitas - vidinė priklausomybė, kuri tik padeda supainioti vartotoją ir yra įgyvendinimo dalis ( valdikliui ).

Dydžio parametras NĖRA priklausomas - tai tik iniciacijos vertė kiekvienam atvejui. IoC yra dandy išorinėms priklausomybėms (pvz., Valdikliui), bet ne vidaus būsenos inicijavimui.

Dar blogiau, jei valdikliui reikia tik 2 iš 4 šios klasės metodų; Aš galiu prisiimti pridėtinę vertę, kai sukuriate valdiklį, net jei jis negali būti naudojamas!

Kaip tai padaryti?

Vienas iš būdų - pereiti tik prie sąsajų, skirtų apibrėžti veiklos sutartį; ir panaikinti naudotojų konstruktorių naudojimą. Kad būtų nuoseklūs, visi objektai turėtų būti prieinami tik per sąsajas ir sukuriami tik per tam tikrą rezoliuciją (pvz., IOC / DI konteinerį). Tik konteineris gauna galimybę kurti dalykus.

Tai susiję su „Widget“ priklausomybe, bet kaip mes inicijuojame „dydį“ nenaudodami atskiro inicijavimo metodo „Foo“ sąsajoje? Naudodami šį sprendimą, praradome galimybę įsitikinti, kad Foo egzempliorius buvo visiškai inicijuotas iki to momento, kai buvo gautas pavyzdys. Bummer, nes man tikrai patinka idėja ir konstruktoriaus įterpimo paprastumas.

Kaip pasiekti garantuotą inicijavimą šiame DI pasaulyje, kai inicijavimas yra DAUGIAU, nei tik išorinės priklausomybės?

3
25 февр. atsakymas pateikiamas 25 vasario mėn. 2010-02-25 09:16 '10, 9:16, 2010-02-25 09:16

Grynas kapsuliavimas yra idealas, kurio niekada negalima pasiekti. Jei visos priklausomybės būtų paslėptos, jums nereikės DI. Tokiu būdu pagalvokite, jei iš tikrųjų turite tam tikrų vertybių, kurias galima išmokti objekto viduje, tarkim, pavyzdžiui, transporto objekto greičio sveikasis skaičius, tada jūs neturite išorinės priklausomybės ir nereikia keisti ar įvesti šios priklausomybės. Šių tipų vidinės būsenos vertės, kurios naudojamos tik privačiomis funkcijomis, yra tai, ką jūs visada norite įterpti.

Bet jei statote automobilį, kuris nori tam tikro variklio objekto, turite išorinę priklausomybę. Galite sukurti šio variklio egzempliorių - pavyzdžiui, naują GMOverHeadCamEngine () automobilio objekto konstruktoriaus viduje, išlaikydami kapsulę, bet kurdami daug daugiau klastingą ryšį su konkrečia GMOverHeadCamEngine klasė, arba galite ją įvesti, kad jūsų automobilio objektas dirbtų agnostiškai (ir patikimiau), pavyzdžiui, su „IEngine“ sąsaja be jokios konkrečios priklausomybės. Nepriklausomai nuo to, ar jūs naudojate TOC konteinerį ar paprastą DI, kad tai būtų pasiektas, tai nėra taip - tai yra tai, kad jūs turite automobilį, kuris gali naudoti daugelio tipų variklius, nesusijęs su bet kuriuo iš jų. Jūsų kodo bazė yra lankstesnė ir mažiau linkusi į šalutinį poveikį.

DI nėra kapsulės pažeidimas, tai yra būdas sumažinti bendravimą, kai kapsuliavimas yra būtinai sugadintas, nes savaime suprantama, beveik kiekviename OOP projekte. Priklausomybės sąsajos įtraukimas į išorę sumažina šalutinį ryšio poveikį ir leidžia jūsų klasėms palikti agnostiką įgyvendinant.

3
14 янв. Atsakymą pateikė Dave Sims 14 sausis 2010-01-14 22:12 '10, 10:12, 2010-01-14 22:12

Kaip Jeff Warner pažymėjo klausimo komentare, atsakymas visiškai priklauso nuo to, kaip apibrėžiate kapsulę.

Atrodo, kad yra dvi pagrindinės stovyklos, o tai reiškia:

  • Viskas, kas susieta su objektu, yra objekto metodas. Taigi, File objektas gali turėti Save , Print , Display , ModifyText ir kt. ModifyText
  • Objektas yra savo mažasis pasaulis ir nepriklauso nuo išorinio elgesio.

Šios dvi apibrėžtys tiesiogiai prieštarauja viena kitai. Jei File objektas gali išspausdinti save, jis labai priklausys nuo spausdintuvo elgesio. Kita vertus, jei jis tiesiog žino apie kažką, kas jam gali atspausdinti ( IFilePrinter arba tam tikra sąsaja), tada File objektas nieko nežino apie spausdinimą, todėl dirbant su juo, objektas priklausys nuo mažiau.

Taigi, jei naudojate pirmąjį apibrėžimą, priklausomybės injekcija sulaužys kapsulę. Tačiau, norint būti sąžiningu, aš nežinau, ar man patinka pirmasis apibrėžimas - tai aiškiai nėra masto (jei taip, MS Word bus viena didelė klasė).

Kita vertus, priklausomybės injekcija yra beveik privaloma, jei naudojate antrą kapsulės apibrėžimą.

3
25 апр. kyoryu atsakymas 25 Bal 2010-04-25 22:12 '10 10:12 val. 2010-04-25 22:12

Tai priklauso nuo to, ar priklausomybė iš tikrųjų yra įgyvendinimo detalė, ar tai, ką klientas norėtų / turėtų žinoti apie tai. Vienas svarbus dalykas yra abstrakcijos lygis, kurio siekia klasė. Štai keletas pavyzdžių:

Jei turite metodą, kuris naudoja spartinimo funkciją po gaubtu, kad pagreitintumėte skambučius, tada talpyklos objektas turi būti atskiras arba kažkas kitas ir neturėtų būti įvestas. Tai, kad talpykla visai naudojama, yra įgyvendinimo detalė, kurią klientai savo klasėje neturėtų rūpi.

Jei jūsų klasė turi išleisti duomenų srautus, tikriausiai tikslinga įvesti išvesties srautą taip, kad klasė galėtų lengvai gauti rezultatus į masyvą, failą arba kažkur kitur, kuriam nors norėtų siųsti duomenis.

Pilkam plotui sakykime, kad turite klasę, kuri atlieka Monte Carlo modeliavimą. Jam reikia atsitiktinumo šaltinio. Viena vertus, tai, kad jam reikia, yra įgyvendinimo detalė, kurioje klientas tikrai nerūpi, iš kur kilo nelaimingas atsitikimas. Kita vertus, kadangi realaus pasaulio atsitiktinių skaičių generatoriai kompromisus tarp atsitiktinumo, greičio ir kt., Kuriuos klientas gali norėti kontroliuoti, ir klientas gali norėti kontroliuoti sėklą, kad gautų pasikartojantį elgesį, injekcija gali būti prasminga. Šiuo atveju siūlau sukurti būdą, kaip sukurti klasę nenurodant atsitiktinių skaičių generatoriaus ir naudodami numatytąją siūlų vertę Singleton. Jei / kai reikalinga tikslesnė kontrolė, nurodykite kitą konstruktorių, kuris leis jums įvesti atsitiktinumo šaltinį.

2
27 янв. atsakymas yra dsimcha 27 jan. 2010-01-27 06:33 '10, 6:33, 2010-01-27 06:33

Kapsuliavimas sunaikinamas tik tuo atveju, jei klasė yra atsakinga už objekto sukūrimą (kuriam reikia žinių apie diegimo detales), ir tada naudoja klasę (kuri nereikalauja žinių apie šias detales). Aš paaiškinsiu, kodėl, pirmiausia, greitą automobilių analogiją:

Kai važinėjau ant seno 1971 m. Combo, galėjau paspaudus akceleratorių, ir jis (šiek tiek) nuėjo greičiau. Man nereikia žinoti, kodėl, bet vaikinai, kurie pastatė Kombi gamykloje, žinojo dėl šios priežasties.

Bet grįžkite į kodavimą. „Kapsuliavimas“ yra „paslėpti įgyvendinimo detales iš to, kas naudoja šį įgyvendinimą“. Kapsuliavimas yra geras, nes įgyvendinimo detalės gali keistis nepažinant klasės.

Naudojant priklausomybės injekciją, konstruktoriaus diegimas naudojamas paslaugų tipo objektams sukurti (o ne objektų / vertybių objektams, modelio būsenai). Bet kokie paslaugų tipo objekto kintamieji yra įgyvendinimo detalės, kurios neturėtų tekėti. pvz., lizdo prievado numeris, duomenų bazės įgaliojimai, kita klasė, kurią reikia skambinti šifravimui, talpykla ir kt.

Konstruktorius klausimai, kai klasė pradžioje sukuriama. Tai vyksta statybos etapo metu, o jūsų DI (arba gamyklos) konteineris jungia visus paslaugų objektus. Konteineris DI žino apie įgyvendinimo detales. Jis žino viską apie įgyvendinimo detales, pvz., „Kombi“ gamyklos vaikinus, jie žino apie uždegimo žvakes.

Veikimo metu sukurtas paslaugų objektas vadinamas aponu, kad būtų atliktas faktinis darbas. Šiuo metu skambinantysis nieko nežino apie įgyvendinimo detales.

Taigi aš nuvažiavau savo Kombi į paplūdimį.

Dabar grįžkite į kapsulę. Jei pasikeičia įgyvendinimo duomenys, klasė, kuri naudoja šį įgyvendinimą vykdymo metu, neturi keistis. Kapsuliavimas nėra pažeistas.

Aš taip pat galiu pasiimti savo naują automobilį į paplūdimį. Kapsuliavimas nėra pažeistas.

Jei pasikeičia įgyvendinimo duomenys, reikia pakeisti DI (arba gamyklos) konteinerį. Pirma, niekada nebandėte paslėpti diegimo detalių iš gamyklos.

2
Atsakymą pateikė WW. 2011-07-07 05:52 07.07.11 val. 5:52 val. 2011-07-07 05:52

Šiek tiek padarius šią problemą, dabar manau, kad priklausomybės įpurškimas (šiuo metu) tam tikru mastu sulaužo kapsulę. Nesuklyskite, nors manau, kad priklausomybės nuo injekcijos naudojimas daugeliu atvejų yra kompromisas.

Būdas, kodėl DI pertrauka į kapsulę, tampa aišku, kai komponentas, kurį dirbate, turi būti pristatytas į „išorinę“ pusę (pagalvokite apie kliento bibliotekos rašymą).

Kai mano komponentas reikalauja, kad subkomponentai būtų įvesti per konstruktorių (arba viešąsias savybes), nėra garantijos