Kodėl mes perduodame objektus, bet ne objektų nariams, funkcijoms?

A klasėje aš turiu FOO() metodą, kuris B be kita ko, mano, kaip B klasės duomenų, argumentai (pvz., Tai yra du plūdurai ir vienas int). Kaip suprantu, geriausia jį įgyvendinti kažką panašaus:

 A->FOO1(B, other_data_x) 

ir ne

 A->FOO2(B.member1, B.member2, B.member3, other_data_x). 

Aš renku vieną, bet ne vienintelį, kad tai, kad ji palieka informaciją apie B nariams prieigą prie FOO1() , todėl padeda paslėpti įgyvendinimo detales.

Bet man įdomu, ar tai tikrai įveda papildomą ryšį tarp A ir B klasių B Pirmajame A klasės klasei turėtų būti žinoma, kad B klasė egzistuoja (pvz., include class_B_header.h ), o jei B nariai keičiasi arba persikelia į kitą klasę arba B klasę, jie yra visiškai neįtraukti, todėl turite pakeisti A ir FOO1() . Atvirkščiai, pastaruoju atveju FOO2() nesvarbu, ar B klasė egzistuoja, iš tikrųjų viskas, kas jai rūpi, yra tai, kad ji turi dvi B.member1 (kurios šiuo atveju susideda iš B.member1 ir B.member2 ) ir int ( B.member3 ). Paskutiniame pavyzdyje taip pat yra nuoroda, tačiau ši nuoroda apdorojama, kai FOO2() gauna skambutį arba kai kurios klasės skambina FOO2() , o ne A arba B apibrėžime.

Manau, kad antroji šio klausimo dalis yra: ar yra geras būdas atskirti A ir B toliau, kai norime įgyvendinti tokį sprendimą kaip FOO1() ?

12
23 мая '13 в 17:19 2013-05-23 17:19 nustatė user1790399 gegužės 23 d. 13 val. 17:19 2013-05-23 17:19
@ 8 atsakymai

Bet man įdomu, ar tai tikrai įveda papildomą ryšį tarp A ir B klasių.

Taip, taip. A ir B glaudžiai sujungti.

Atrodo, kad manote, kad paprastai manoma, jog reikia perkelti objektus, o ne šių objektų narius. Nesu tikras, kaip jūs turėjote šį įspūdį, bet ne. Jei turite nusiųsti objektą ar šio objekto narius, tai visiškai priklauso nuo to, ką bandote daryti.

Kai kuriais atvejais būtina ir pageidautina turėti glaudų ryšį tarp dviejų objektų ir kitais atvejais - ne. Jei yra bendroji taisyklė, kuri taikoma čia, norėčiau pasakyti, jei kažkas yra priešingai nei siūlėte:

Pašalinkite priklausomybes, kai tik įmanoma, bet niekur.

18
23 мая '13 в 17:26 2013-05-23 17:26 John Dibling atsakymas gegužės 23 d. 13 d. 17:26 val. 2013-05-23 17:26

Iš tiesų nėra tokios visuotinės teisės, o kita yra visuotinė. Iš esmės tai yra klausimas, kas geriau atspindi jūsų tikrąjį ketinimą (kurio neįmanoma atspėti meta sintetinių kintamųjų pagalba).

Pavyzdžiui, jei parašiau funkciją „read_person_data“, tikėtina, kad tam tikras „asmeninis“ objektas turėtų būti pasirinktas kaip duomenų, kuriuos jis turėtų skaityti, tikslas.

Kita vertus, jei turiu funkciją „perskaityti dvi eilutes ir int“, tikriausiai turėtų būti dvi eilutės ir int, net jei (pavyzdžiui), aš naudoju jį perskaityti pirmąjį ir pavardę bei darbuotojų skaičių (t.y. bent dalį asmens duomenų).

Yra du pagrindiniai aspektai: pirma, funkcija, panaši į pirmąjį, kuris daro kažką prasmingo ir loginio, paprastai yra geriau nei viena, pvz., Paskutinė, kuri yra šiek tiek didesnė už bet kokį kartu vykdomų veiksmų derinį.

Antra, jei turite tokią situaciją, tai tampa atviru klausimu, ar ši funkcija neturėtų būti (bent jau logiškai) jūsų A dalis, o ne kažkas atskira, dirbant su A , ji nėra apibrėžta jokiu tai reiškia, bet jūs galite tiesiog pažvelgti į blogą segmentavimą tarp atitinkamų klasių.

13
23 мая '13 в 17:28 2013-05-23 17:28 atsakymą pateikė Jerry Coffin gegužės 13 d., 17:28, 2013-05-23 17:28

Sveiki atvykę į inžinerijos pasaulį, kur viskas yra kompromisas, o teisingas sprendimas priklauso nuo to, kaip turėtų būti naudojamos jūsų klasės ir funkcijos (ir kokia jų reikšmė).

Jei foo() yra konceptualiai kažkas, kurio rezultatas priklauso nuo float , int ir string , tuomet yra teisinga priimti float , int ir string . Nesvarbu, ar šios vertės yra klasės (galbūt B , bet ir C arba D ) narės, nesvarbu, nes semantiškai foo() nėra apibrėžta pagal B

Kita vertus, jei foo() konceptualiai kažkas, kurio rezultatas priklauso nuo B būklės, pavyzdžiui, dėl to, kad ji įgyvendina abstrakciją per šią būseną, tada padaro jį B tipo objektu.

Dabar taip pat tiesa, kad gera programavimo praktika yra leisti funkcijoms atlikti nedidelį skaičių argumentų, jei tai įmanoma, pasakyčiau iki trijų be perdėtumo, todėl, jei funkcija veikia logiškai su keliomis reikšmėmis, galbūt norėsite grupuoti šias vertes į duomenų ir perduoti šio duomenų struktūros atvejus foo() .

Dabar, jei vadinate šią duomenų struktūrą B , sugrįšime prie pradinės problemos - bet su semantinio skirtumo pasauliu!

Vadinasi, ar foo() būti trys vertybės, ar B atvejis daugiausia priklauso nuo to, ką foo() ir B konkrečiai reiškia jūsų programoje ir kaip jie bus naudojami, nepriklausomai nuo to, ar kompetencijos ir pareigos yra logiškai susijusios, ar ne .

5
23 мая '13 в 17:31 2013-05-23 17:31 atsakymą pateikė Andy Prowl, gegužės 23 d., 13 d., 17:31 val. 2013-05-23 17:31

Atsižvelgiant į šį klausimą, tikriausiai galvojate apie neteisingą kelią.

Naudojant klasę, turėtumėte domėtis jos viešąja sąsaja (paprastai susidedančia tik iš metodų), o ne jo duomenų nariais. Paprastai duomenų nariai turi būti klasikiniai, todėl jūs net neturėsite prieigos prie jų.

Pagalvokite apie klases kaip fizinius objektus, tarkim, kamuolį. Galite žiūrėti į kamuoliuką ir pamatyti, kad jis yra raudonas, bet jūs negalite tiesiog nustatyti kamuoliuko spalvos, o ne mėlynos spalvos. Turėsite atlikti veiksmus ant rutulio, kad jis būtų mėlynas, pvz., Tapydami jį.

Grįžkite į savo klases: tam, kad A atliktų kai kuriuos veiksmus B, A turės žinoti kažką apie B (pvz., Kad kamuolys būtų spalvotas, kad pakeistų jo spalvą).

Jei norite, kad A dirbtų su kitais objektais, išskyrus B klasės objektus, galite naudoti paveldėjimą, kad gautumėte A reikalaujamą sąsają į I klasę, o tada leiskite B klasei ir kitai C klasei, paveldintai iš A. B ir C klasės.

3
23 мая '13 в 17:32 2013-05-23 17:32 atsakymas pateikiamas zennehoy gegužės 23 d., 13 val. 17:32 pm 2013-05-23 17:32

Manau, kad tai tikrai priklauso nuo to, ką norite atlikti. Nėra nieko blogo perleidžiant kelis klasės narius iš funkcijos. Tai tikrai priklauso nuo to, ką reiškia „FOO1“ - ar FOO1B naudodamas other_data_x aiškiau, ką norite daryti.

Pavyzdžiui, vietoj savavališkų A, B, FOO ir pan. mes sukuriame „tikrus“ pavadinimus, kad būtų prasminga:

 enum Shapetype { Circle, Square }; enum ShapeColour { Red, Green, Blue } class Shape { public: Shape(ShapeType type, int x, int y, ShapeColour c) : x(x), y(y), type(type), colour(c) {} ShapeType type; int x, y; ShapeColour colour; ... }; class Renderer { ... DrawObject1(const Shape  float magnification); DrawObject2(ShapeType s, int x, int y, ShapeColour c, float magnification); }; int main() { Renderer r(...); Shape c(Circle, 10, 10, Red); Shape s(Square, 20, 20, Green); r.DrawObject1(c, 1.0); r.DrawObject1(s, 2.0); // ---- or --- r.DrawObject2(c.type, cx, cy, c.colour, 1.0); r.DrawObject2(s.type, sx, sy, s.colour, 1.0); }; 

[Taip, tai vis dar gana kvailas pavyzdys, tačiau tikslinga aptarti temą, tada objektai turi tikrus vardus]

DrawObject1 turi žinoti viską apie formą ir, jei pradėsime reorganizuoti duomenų struktūrą (išsaugoti x ir y į vieno nario kintamąjį, vadinamą point ), jis turi pasikeisti. Bet tai tikriausiai tik keli nedideli pakeitimai.

Kita vertus, „ DrawObject2 mes galime pertvarkyti viską, kas mums patinka, formų klasei - netgi ištrinti viską kartu ir turėti x, y, formą ir spalvą atskiruose vektoriuose [ne itin gera idėja, bet jei manome, kad kažkur išspręsti tam tikrą problemą, tada mes galime tai padaryti].

Iš esmės tai atsitinka, kas yra prasminga. Mano pavyzdyje tikriausiai yra prasminga perduoti Shape objektą DrawObject1 . Tačiau taip nėra, ir, žinoma, yra daug atvejų, kai to nenorėjote.

2
23 мая '13 в 17:44 2013-05-23 17:44 atsakymą pateikė Mats Petersson gegužės 13 d., 17:44 2013-05-23 17:44

Yra keletas priežasčių, kodėl norite perkelti klasę vietoj atskirų narių.

  • Tai priklauso nuo to, ką vadinama funkcija turi daryti su argumentais. Jei argumentai yra gana izoliuoti, norėčiau perduoti narį.
  • Kai kuriais atvejais gali tekti perduoti daug kintamųjų. Šiuo atveju efektyviau pakuoti juos į vieną objektą ir juos perkelti.
  • Sandarinimas. Jei jums reikia kažkaip prijungti vertes viena kitai, gali būti klasė, susieta su šia asociacija, užuot naudojusi ją kodu, kur jums reikia vieno nario.

Jei nerimaujate dėl priklausomybių, galite įdiegti sąsajas (pvz., „Java“ arba „C ++“, naudojant abstrakčias klases). Tokiu būdu galite sumažinti priklausomybę nuo konkretaus objekto, tačiau įsitikinkite, kad jis gali tvarkyti reikiamą API.

2
23 мая '13 в 17:30 2013-05-23 17:30 atsakymą davė Devolus , gegužės 23 d., 13 val., 17.30 val. 2013-05-23 17:30

Kodėl jūs perduodate objektus vietoj primityvių?

Tai klausimas, kurį tikiuosi iš @se programuotojų; vis dėlto ..

Mes perduodame objektus vietoj primityvių, nes stengiamės sukurti aiškų kontekstą, atskirti tam tikrą požiūrį ir išreikšti aiškų ketinimą.

Pasirinkimas perduoti primityvius, o ne visateisius, turtingus konteksto objektus, sumažina abstrakcijos laipsnį ir plečia funkcijos sritį. Kartais tai yra tikslai, bet paprastai tai yra dalykai, kuriuos reikėtų vengti. Maža sanglauda yra įsipareigojimas, o ne turtas.

Vietoj to, kad dirbtumėte su susijusiais dalykais per pilnavertį turtingą objektą su primityviais, dabar mes elgiamės ne tik kaip savavališkos vertybės, kurios gali arba negali būti griežtai susijusios viena su kita. Be konkretaus konteksto mes nežinome, ar primityvų derinys bet kuriuo metu yra suderinamas, jau nekalbant apie tiesioginę dabartinės sistemos būseną. Bet kokie turtingo konteksto objekto teikiami apsaugos būdai būtų prarasti (arba dubliuojami neteisingoje vietoje), kai pasirinkome funkcijų apibrėžime primityvius, kai galėtume pasirinkti turtingą objektą. Be to, bet kokie įvykiai ar signalai, kuriuos mes paprastai keliame, stebime ir elgiamės keičiant bet kokias vertybes turtingame kontekste, sunkiau pakelti ir sekti tinkamu laiku, kai jie svarbūs dirbant su paprastais primityviais.

Naudojant daiktus virš primityvių, palengvinama sanglauda. Sanglauda yra tikslas. Dalykai kartu lieka kartu. Gerai laikomasi natūralios funkcijos priklausomybės nuo šios grupės, parametrų užsakymo ir sąveikos aiškiame kontekste.

Objektų naudojimas primityvuose nebūtinai padidina ryšio tipą, su kuriuo mes labiausiai rūpi. Mums reikia daugiau nerimauti, kokia komunikacija atsiranda, kai išoriniai abonentai diktuoja pranešimų formą ir seką, ir mes toliau parduodame savo nustatytas taisykles kaip vienintelį būdą žaisti.

Užuot įvedę viską, turėtume pastebėti aiškų „pasakyti“, kai matome. Tarpinės programinės įrangos tiekėjai ir paslaugų teikėjai tikrai nori, kad visi prisijungtume ir glaudžiai integruotume mūsų sistemą. Jie nori stiprios priklausomybės, glaudžiai susietos su mūsų pačių kodais, todėl nuolat grįžtame. Tačiau mes esame protingesni. Jei nesate protingas, bent jau galime būti pakankamai patyrę, kad galėtume eiti šiuo keliu ir pamatyti, kas vyksta. Mes nedalyvaujame pasiūlyme, leidžiant pardavėjams koduoti elementus įsiveržti į kiekvieną kampą ir kreko, žinodami, kad negalime nusipirkti rankos, nes jie sėdi ant žetonų krūvos, ir atvirai kalbant, mūsų ranka nėra tokia gera. Vietoj to, mes sakome, kad tai yra tai, ką aš ketinu daryti su jūsų tarpinės programinės įrangos, ir mes sukūrėme ribotą adaptyvią sąsają, kuri leidžia tęsti žaidimą, bet nededa to ūkio. Tai darome, nes kitoje rankoje mes galime patekti į kitą tarpinės programinės įrangos teikėją ar paslaugų teikėją.

Skirtingai nuo populiarios pokerio metaforos, idėja pabėgti iš kombinacijos, kai ji pati pristatys, jums kainuos. Bėgimas nuo labiausiai susipynusio ir brangiausio ryšio tikriausiai yra protingas dalykas, jei ketinate ilgą laiką pasilikti žaidime ir linkę žaisti su kitais tiekėjais ar tiekėjais ar įrenginiais, kuriuos galite valdyti.

Galiu daug daugiau pasakyti apie kontekstinių objektų prijungimą ir primityvų naudojimą, pavyzdžiui, teikiant prasmingus lanksčius testus. Vietoj to, aš norėčiau pasiūlyti konkrečių autorių, pavyzdžiui, dėdės Bobo Martino, kodavimo stiliaus rodmenis, bet dabar aš jų nepamirsiu.

1
23 мая '13 в 22:37 2013-05-23 22:37 atsakymą Justinas pateikė gegužės 23 d., 13 val. 10:37 2013-05-05 22:37

Sutikimas su pirmuoju respondentu čia ir jūsų klausimu apie „ar yra kokiu nors kitu būdu“.

Kadangi jūs pravažiuojate B klasės egzempliorių į A grupės FOO1 metodą, galima pagrįstai manyti, kad B funkcionalumas jam nėra visiškai unikalus, ty gali būti ir kitų būdų, kaip įgyvendinti bet kurį B (jei B yra POD be savo logikos, tada nėra prasmės bandyti atskirti, nes A vis dar turi daug žinoti apie B).

Todėl galite atskirti A nuo B nuo nešvarių paslapčių, pakeliant viską B sąsajoje A, o tada A apima „BsInterface.h“. Tai rodo, kad gali būti C, D ... ir kitų variantų, kaip daryti tai, ką B daro A. Jei ne, tuomet turėtumėte savęs paklausti, kodėl B yra klasė už A, pirmiausia ...

Galų gale, viskas ateina į vieną dalyką; Tai turėtų prasmės ...

Aš visuomet paverčiu problemą į galvą, kai susiduriu su tokiomis filosofinėmis diskusijomis (dažniausiai su savimi); Kaip naudoti A-> FOO1? Ar prasminga, kad skambinimo kodas būtų susijęs su „B“ ir ar aš visuomet kartu su A ir B, nes A ir B yra naudojami kartu?

tai yra; jei norite būti smulkmeniškas ir rašyti švarų kodą (+1 prie to), žengkite žingsnį atgal ir taikykite „išlaikyti paprastą“ taisyklę, tačiau visada leiskite naudoti kryžminimus, kurių reikia norint sukurti savo kodą.

Na, tai mano nuomonė.

0
23 мая '13 в 17:36 2013-05-23 17:36 atsakymas pateikiamas „ SonarJetLens“ gegužės 23 d., „13, 17:36 pm 2013-05-23 17:36

Kiti klausimai apie „ arba užduoti klausimą