Jeg er i en knibe. Jeg arbejder i et lager, der foretager mange databaseændringer inden for dets funktioner.

Den funktion, jeg har at gøre med, returnerer responsid (men fra en transformation, ikke fra nogen databasehandling). Som en bivirkning tilføjer det imidlertid et objekt, der indeholder disse responsIds til databasen.

Så skal jeg navngive det:

  • getResponseIds: Dette fremhæver returværdierne. Det er en meget funktionel måde at tænke på, men hvis jeg har funktion postToDB, giver det ingen mening at bruge getStatusOfPost
  • addResponseIdToDB: Dette fremhæver bivirkningen, selvom jeg virkelig tror, at mange af mine funktioner bare fungerer på databasen (og har tendens til ikke at returnere noget)
  • getAndAddResponseIdsToDB: Meget informativ, men meget lang.

Hvad er fordele og ulemper ved ovenstående forslag? Eller kan du selv komme med et bedre forslag?

Kommentarer

  • Hvad med persistResponseIds?
  • Du kan blive overrasket over, hvor langt den funktionelle publikum er nået med ækvivalenten med getStatusOfPost 🙂
  • getAndSaveResponseIds
  • responseIds = createResponseIds() virker klar og kortfattet. Du get() noget der ikke eksisterede ‘ t før, så create er det passende verb. De nyoprettede ting er, hvad du ‘ forventer, at en create() -funktion vender tilbage. Eller måske savner jeg ‘ noget indlysende?
  • @fattie Jeg er enig i, at koden skal være selvkommenterende. Kommentarer skal ikke fortælle dig, hvad koden gør. Kommentarer skal fortælle dig, hvorfor koden findes. Hvis refactoring af koden betyder refactoring af den ‘ s kommentarer, har du dårlige kommentarer. Jeg er enig i, at navne kan være lange fulde sætninger. Et godt navn skal fortsætte med at kigge inde i en funktion fra at overraske dig. Det skal ikke ‘ ikke fortælle dig, hvordan du implementerer det. Det skal fortælle dig, hvad klienten forventer. Gør det, og du får en god abstraktion, der er værd.

Svar

Et funktionsnavn, der indeholder and er på forkert abstraktionsniveau .

Jeg læner mig mod addResponseIdToDB() fordi ellers er bivirkningen en fuldstændig overraskelse. Dog:

responseIds = addResponseIdToDB(); 

efterlader ingen overraskede.

princip om adskillelse af ansvar for adskillelse af kommandoer hævder, at dette ikke bør være den eneste måde at få responsId-objektet på. Der skal også være en forespørgsel, der ikke ændrer DBen, der kan få dette objekt.

I modsætning til Bertrand Meyer , tror jeg ikke, det betyder, at tilføjelsen skal returneres ugyldig. Bare betyder, at der skal eksistere en ækvivalent ren forespørgsel og være let at finde, så DB ikke misbruges unødigt ved brug af tilstandsændrende forespørgsler.

Da getResponseIds() skulle eksistere og ikke tale med databasen, er det bedste navn for en metode, der begge gør, faktisk addToDB(getResponseId()). Men det er bare hvis du vil have al funktionel sammensætning om det.

Kommentarer

  • Tilføjelse til hvad der er blevet sagt, men tilføjelse af et ord, der ikke er brugt ‘, vil jeg sige, at ” og ” er passende, når en funktion gør noget, der er atomisk . Hvis der er en fordel, der opnås ved, at to ting sker atomisk / sammen / på én gang, så er det eksplicit, hvad disse to ting er i funktionen navn er godt. Så jeg er enig med dit første udsagn generelt, men er uenig uden nogen kvalifikationer.
  • … efterlader ingen overrasket. “. Jeg er uenig, jeg ‘ er tilbage overrasket over, at en boolesk variabel hedder responseIds og jeg skal derefter foretage et mentalt skift for at indse, at det tilføjer og får alt i én. Men jeg er uenig med @tintintong at getAndAddResponseIdsToDB er for lang; det ‘ er omkring halvdelen af en lang række af mine funktionsnavne. Hvis en funktion gør to ting, skal du sige det i dets navn.
  • @Luaan OK, hvad skal du omdøbe getAndIncrement til da? otherIncrement?
  • @Luaan Hvilken værdi returnerer så Increment, værdien forøgelse eller stigning efter? Det ‘ er ikke klart, hvad det ville være uden at dykke ned i dokumenter.Jeg synes incrementAndGet og getAndIncrement er langt bedre metodenavne, hvilket jeg mener giver en god sag for ” OG ” i metodenavne, i det mindste i det specifikke tilfælde af metoder, der har bivirkninger, der kan påvirke returværdien ..
  • @ Luaan Når du siger ” Increment returnerer den inkrementerede værdi ” mener du værdien for increment? Gotcha, misforstod dig først .. hvilket faktisk er et godt eksempel på, hvorfor getAndIncrement og incrementAndGet er bedre navne end bare increment, selv når det betragtes individuelt

Svar

Som andre har nævnt, bruger and i et funktionsnavn automatisk, betyder det, at en funktion laver mindst to ting, hvilket normalt indikerer, at den laver for meget eller gør noget på det forkerte sted.

Brug af and -klausulen i et funktionsnavn kan dog efter min erfaring give mening, når der er nogle trin i et bestemt flow, der skal udføres i rækkefølge, eller når hele beregningen bliver meningsfuld.

I numerisk computing kan dette give meget mening:

normalizeAndInvert(Matrix m)

Jeg mener, hvem ved det, ikke? Hvis du har brug for at beregne nogle … siger, lysbaner i computergrafik og på et cert i scenen skal du normalisere og invertere en bestemt matrix, og du finder dig selv konstant ved at skrive:

m = normalize(m), efterfulgt af invert(m), som et simpelt eksempel, at introducere abstraktion af normalisering og invertering, kan være godt set fra et læsbarhedsperspektiv.

Når man taler semantisk, ting som divideAndConquer() osv. frarådes sandsynligvis eksplicit at blive skrevet ud, men vel, And der er grundlæggende nødvendigt.

Kommentarer

  • Funktioner gør regelmæssigt mere end én ting, men det burde være små ting tilføje op til en stor ting, der ‘ er meningsfuldt på et højere niveau. normalizeAndInvert kunne være computeLighting og divideAndConquer kunne være applyMachiavelli
  • Det er klart, at jeg bare siger, at der først oprettes en funktion med en ” og ” i navnet kan komme som en naturlig abstraktion baseret på en bestemt kontekst. Refactoring via ” Omdøbsmetode ” skal komme umiddelbart efter;)
  • @BrunoOliveira Medmindre de to trin undertiden er selv en komponent af mange ting, omdøb ikke ‘ t den rigtige tilgang. Og skal være sjælden, men aldrig at bruge det betyder, at du ‘ gentager dig selv.

Svar

Jeg vil sige, at det generelt er fint, men det er ikke acceptabelt i alle tilfælde.

For eksempel kan man argumentere imod and i et funktionsnavn baseret på enkeltansvarsprincippet aka S i SOLID – fordi and kunne antyde flere ansvarsområder. Hvis and virkelig betyder, at funktionen laver to ting, så vil du sandsynligvis tænke nøje over, hvad der sker.

Et eksempel på et bibliotek, der bruger and i funktionsnavne kan findes i Java “s concurrency og her and er faktisk en meget vigtig del af, hvad der foregår, og svarer nøje til det, du beskriver i dit indlæg, hvor staten ændres og tilstand returneres. Så tydeligt er der nogle mennesker ( og nogle brugssager) hvor det “anses for acceptabelt.

Kommentarer

  • and kunne antyde flere ansvarsområder “. Og i dette tilfælde indikerer det faktisk det. Funktionen får nogle svar-ider fra et eller andet sted, skriver dem til en database og returnerer dem. Behovet for at ” og ” burde faktisk i det mindste rejse ideen med OP om, at der er behov for to funktioner: en for at få IDerne og en til at skrive dem til databasen.
  • Mens jeg er enig i, at and er en kodelugt, virker den grundlæggende adfærd legitim: Det er generelt ikke en dårlig idé at returnere nogle ekstra værdier fra en metode, der tilfældigvis er nødvendige (mellemliggende) resultater af udførelsen af dens hovedfunktion. Hvis denne metodes hovedfunktion er at tilføje svar-iderne til databasen, er det endvidere bare en pæn, ubesværet brugbarhedsfunktion ved metoden at returnere de ider, den har tilføjet til den, der ringer op.
  • Jeg tror ikke ‘ t mener, at dette nødvendigvis krænker SRP. Du har altid brug for funktioner, der gør flere ting. Den vigtige del er, at disse funktioner kalder en anden funktion for hver ting, de vil gøre i stedet for at indeholde den nødvendige kode direkte.
  • Hvis funktionen gør to ting, skal navnet afspejle den.

Svar

Det er faktisk et vanskeligt spørgsmål at besvare som mange kommentarer kan vidne om. Folk synes at have modstridende meninger og råd om hvad der er et godt navn.

Jeg vil gerne tilføje mine 2 cent til denne 2 måneder gamle tråd, fordi jeg tror, jeg kan tilføje flere farver til det, der allerede blev foreslået.

Navngivning er en proces

Alt dette minder mig om den fremragende guide til 6 trin: Navngivning som en proces .

Et dårligt funktionsnavn er et, der er overraskende, og du er klar over, at du ikke kan stole på. God navngivning er svært at få lige ved første skud. Det bliver lettere med erfaring.

6 iterative trin til at opbygge et godt navn

  1. Erstat overraskende navn med indlysende vrøvl ligesom appleSauce(). Det lyder fjollet, men det er midlertidigt, og det gør det indlysende, at navnet ikke kan stole på.
  2. Få et ærligt navn , baseret på hvad du forstår ud fra, hvad funktionen gør. Sig, at du endnu ikke forstod indsættelsen i DB-del, getResponseIdsAndProbablyUseDb ville være en mulighed.
  3. Få helt ærligt , så funktionsnavnet siger alt, hvad funktionen gør (getAndAddResponseIdsToDB eller getResponsesButAlsoAddCacheOfTheResponsesToTheDb fra @Fattie er gode eksempler)
  4. Kom til “gør det rigtige” den del, hvor du deler din funktion langs “AND”. Så du har faktisk getResponseIds og addResponseIdsToDb.
  5. Gå til et “Intention Revealing” navn som løser punktet med “Jeg vil altid have svar-idet I indsat i DB, når jeg har gjort det “. Stop med at tænke på implementeringsdetaljerne og opbyg en abstraktion, der bruger de 2 mindste funktioner til at opbygge noget andet. Dette er det højere abstraktionsniveau, der er nævnt af @candied_orange. For e xample createResponseIds kunne gøre det og vil være sammensætningen af getResponseIds og addResponseIdsToDb.
  6. Gå til din domæne-abstraktion . Dette er svært, det afhænger af din virksomhed. Det tager tid at lytte til dit forretningssprog for at få det rigtigt. Til sidst kan du ende med konceptet med en Response og Response.createIds() ville give mening. Eller måske er ResponseId noget værdifuldt, og du vil have en fabrik til at skabe mange. Indsættelse i DB ville være en implementeringsdetalje for et arkiv osv. Ja, designet ville være mere abstrakt. Det ville være rigere og lade dig udtrykke mere. Ingen uden for dit team kan fortælle dig, hvad den rigtige domæneabstraktion skal være i din situation. Det afhænger .

I dit tilfælde har du allerede fundet ud af 1 og 2, så du skal sandsynligvis i det mindste gå til 3 (brug “OG”) eller længere. Men at gå videre er ikke kun et spørgsmål om navngivning, du splitter faktisk ansvaret.

De forskellige forslag her er således gyldige

I det væsentlige:

  • Ja, overraskende funktionsnavne er forfærdelige, og ingen vil tackle det
  • Ja, “AND” er acceptabelt i et funktionsnavn, fordi det er bedre end et vildledende navn
  • Ja, abstraktionsniveauet er et beslægtet koncept, og det betyder noget

Du behøver ikke gå til trin 6 med det samme, det er fint at blive på trin 2, 3 eller 4, indtil du ved bedre!


Jeg håber, det vil være nyttigt at give et andet perspektiv på, hvad der ser ud som modstridende rådgivning. Jeg tror, at alle sigter mod det samme mål ( ikke-overraskende navne) men stop på forskellige stadier baseret på deres egen erfaring.

Glad for at svare, hvis du har spørgsmål 🙂

Svar

Din funktion gør to ting:

  1. Får et sæt ider via en transformation og returnerer dem til den, der ringer op,
  2. Skriver det sæt ider til en database.

Husk princippet om et enkelt ansvar her: du skal sigte mod en funktion at have et ansvar, ikke to. Du har to ansvarsområder, så har to funktioner:

getResponseIds – Får et sæt ider via en transformation og returnerer dem til den, der ringer op
addResponseIdToDB – Tager et sæt ider og skriver dem til en database.

Og hele spørgsmålet om, hvad man skal kalde en enkelt funktion med flere ansvarsområder, forsvinder, og fristelsen til at placere and i funktionsnavne forsvinder også.

Som en ekstra bonus, fordi den samme funktion ikke længere er ansvarlig for to ikke-relaterede handlinger, kunne getResponseIds flyttes ud af din repokode (hvor den ikke hører hjemme da det ikke udfører nogen DB-relateret aktivitet), i en separat klasse / modul på forretningsniveau osv.

Kommentarer

  • At ‘ er helt sikkert sandt, men du finder dig selv at gentage et uddrag ved hjælp af disse to SRP-metoder mange gange, så skal du sandsynligvis skrive en triviel metode, der gør dette, så du TØRR, skal ikke ‘ ikke dig?
  • @maaartinus, hvis du finder dig selv at kalde disse to metoder mange steder, så har du sandsynligvis et problem med manglende samhørighed i din kode. Oprettelse af en ” TØR ” -funktion maskerer bare denne manglende samhørighed.

Svar

At have et sammensat funktionsnavn er acceptabelt, så hvilket navngivningsskema du bruger afhænger af din kontekst. Der er purister, der vil bede dig om at bryde det op, men jeg vil argumentere for noget andet.

Der er to grunde til at forsøge at undgå sådanne ting:

  • Renhed – En funktion der gør to ting, skal konverteres til to funktioner, der hver gør en ting.
  • Lexisk størrelse – en bigUglyCompositeFunctionName bliver svær at læse.

Renhedsargumentet er typisk det, der er fokuseret på. Som en vag generel heuristik er det en god ting at bryde funktioner. Det hjælper med at opdele, hvad der foregår, hvilket gør det lettere at forstå. Du er mindre tilbøjelige til at lave “kloge” tricks med deling af variabler, der fører til sammenkobling mellem de to adfærd.

Så det at spørge dig selv er “skal jeg alligevel gøre det?” Jeg siger, at opdelingen er en vag heuristik, så du behøver virkelig kun et anstændigt argument for at gå imod det.

En væsentlig årsag er, at det er nyttigt at tænke på de to operationer som en. Det endelige eksempel på dette findes i atomoperationer: compare_and_set. compare_and_set er en grundlæggende hjørnesten i atomerne (som er en måde at gøre multithreading uden låse på), som er vellykket nok til at mange er villige til at tænke på det som the hjørnesten. Der er “og” i det navn. Der er en meget god grund til dette. Hele pointen med denne operation er, at den foretager sammenligningen og indstillingen som en udelelig handling. Hvis du delte det op i compare() og set(), ville du besejre hele grunden til, at funktionen eksisterede i første omgang, fordi to compares() kan forekomme ryg mod ryg før set() s.

Vi ser dette også i performance. Mit foretrukne eksempel er fastInvSqrt . Dette er en berømt algoritme fra Quake, der beregner 1 / sqrt (x). Vi kombinerer operationerne “invers” og “kvadratrod”, fordi det giver os en massiv præstationsforøgelse. De foretager en tilnærmelse fra Newton, som udfører begge operationer i et enkelt trin (og tilfældigvis gør det i heltal matematik snarere end flydende punkt, hvilket betyder noget i æraen). Hvis du skulle gøre inverse(sqrt(x)), ville det være meget langsommere!

Der er nogle gange, hvor resultatet er mere klart. Jeg har skrevet adskillige APIer, der involverer threading, hvor man skal være frygtelig forsigtig med ting som deadlocks. Jeg ville ikke have, at min bruger skulle være opmærksom på de interne implementeringsoplysninger (især da jeg måske ændrede dem), så jeg skrev API med et par “og” funktioner, som forhindrede brugeren i at skulle holde en lås i mere end et funktionsopkald. Det betyder, at de aldrig har brug for at vide, hvordan jeg håndterede den multitrådede synkronisering under emhætten. Faktisk var de fleste af mine brugere ikke engang klar over, at de var i et multitrådet miljø!

Så selvom den generelle regel er at bryde ting op, kan der altid være en grund til at parre dem sammen. For eksempel , kan din bruger bryde din arkitektur ved at tilføje til en database flere gange uden “får” imellem? Hvis hvert af svarene, der er logget ind i DBen, skal have en unik identifikator, kan du komme i problemer, hvis brugeren skulle gøre dem vildt nilly. Ligeledes vil du nogensinde lade en bruger “få” uden at logge resultaterne i DB? Hvis svaret er “ja”, skal du bryde dem op, så brugeren kan få adgang til “get” -funktionen. Men hvis din applikations sikkerhed er brudt, hvis brugeren kan “få” uden, at resultatet er logget i DB, skal du virkelig holde funktionerne sammen.

Hvad angår de leksikale problemer, skal dit funktionsnavn beskrive, hvad brugeren har brug for at vide om det. Lad os starte med “get” -delen.Det er ret nemt at fjerne “get” fra et funktionsnavn, fordi alle dine eksempler viser en opgave: int id = addResponseIdToDB() “. Alle steder, hvor funktionen bruges, slutter du op med at dokumentere det faktum, at funktionen returnerede en værdi.

Ligeledes kan “tilføj” være valgfri. Vi bruger udtrykket “bivirkning” som et altomfattende udtryk, men der er forskellige nuancer af det. Hvis DB-poster kun er en log, er der muligvis ingen grund til at fremhæve den. Vi ser aldrig funktioner som playMusicAndSendPersonalInformationToOurCorporateServers. Det er bare playMusic, og hvis du er heldig, kan dokumentationen muligvis nævne noget om en stikketforbindelse over internettet. På den anden side, hvis det forventes, at brugerne kalder denne funktion med det formål at tilføje ting til DBen, er “tilføj” afgørende. Tag det ikke ud.

Nu har jeg skrevet mange grunde til at gøre enhver mulig kombination af det, du beder om. Jeg har bevidst ikke besvaret spørgsmålet, fordi der skal vælges. Det er ikke en regel at følge. Du opretter en API.

Når det er sagt, er mit instinkt, at addResponseIdToDB sandsynligvis vil være det bedste svar. I de fleste tilfælde er “get” -delen en åbenbar nok bivirkning til, at den ikke tjener den ekstra støj, der skyldes, at den indtastes overalt. Der er dog nogle få steder, hvor jeg måske finder det vigtigt:

  • Hvis “get” er dyrt – Hvis du skal foretage en “get”, der involverer at hente noget over internettet og derefter tilføje det til en lokal cache-DB, er “get” under alle omstændigheder vigtig. Det er den ting, som brugeren prøver at gøre.
  • Hvis du har brug for at gøre det klart, at brugeren skal være opmærksom på værdien. Hvis brugeren ønsker at bruge variablen, vil de indse, at APIen returnerer den, og de vil bruge den. Du vil dog være opmærksom på den bruger, der ikke ved, at de har brug for det. Hvis du f.eks. Skal “lukke” denne handling med ID senere for at frigøre hukommelse, kan du måske gøre opmærksom på, at du “laver noget. I disse tilfælde vil du måske se på et andet verbum end “get”. “get” indebærer ofte idempotente funktioner (at kalde det igen gør ikke noget). Hvis det returnerer ressourcer, er andre verb som “create” eller “erhverve” pæne. Denne særlige fejlmekanisme er et stort problem i C, når man udfører undtagelseshåndtering C mangler fangst / kastemekanismen for C ++, så det er afhængig af returkoder. Udviklere er berømte for simpelthen ikke at kontrollere disse returkoder og komme i virkelig dårlige situationer som bufferoverløb på grund af det.
  • Symmetri – Nogle gange designer du en API, så den har en leksikal symmetri. Du opsætter ordene, så de kommer i par eller andre mønstre, og udformer kommandoerne, så det er let at visuelt identificere om mønsteret er fulgt eller ej. Jeg siger, at dette er sjældent, men det er ikke uhørt. Der er en grund til at lukke XML-tags gentage tagnavnet (såsom < foo > og < / foo >).

Kommentarer

  • For folk der ikke ‘ kender ikke matematikken: Både 1 / x og sqrt (x) er ret langsomme funktioner. Ved hjælp af nogle smarte matematikker kan vi beregne 1 / sqrt (x) hurtigere end enten en division eller en kvadratrod. Faktisk så meget hurtigere, at den hurtigste måde at beregne sqrt (x) er at beregne 1 / sqrt (x) med en smart implementering og multiplicere resultatet med x.
  • Ligesom i fysik, hvor elementære enheder ikke længere er meter og sekund, men sekund og lyshastighed, fordi vi kan måle lysets hastighed med mere præcision end længden på en meter.

Svar

Hvad er det ultimative hensigt af denne metode od? Hvorfor føje disse transformerede ider til DB, er det med henblik på cache? Jeg vil arbejde under den antagelse.

Det lyder som, at metodens hensigt egentlig bare er at få de transformerede respons-ider (enten at udføre transformeringen eller få dem fra en cache), og så er der dit metodens navn:

getTransformedResponseIds eller mindre ordet getResponseIds()

En metode med dette navn kan cache eller måske ikke, og det kan dokumenteres, men dit metodenavn burde ikke binde dig til en bestemt implementering. Hvad hvis du beslutter at stoppe med at bruge en DB og i stedet cache dem andre steder som f.eks. midlertidigt i hukommelsen?

Bivirkninger skulle være netop det, bivirkninger, de skulle sandsynligvis dokumenteres, men de skulle ikke virkelig påvirke funktionens kerneformål (eller succes) eller navn. Hvis det ikke lykkes at hente værdien fra cachen (eller hvis du konfigurerer at springe cache over), der ikke burde være et kritisk problem, skal metoden bare genberegne, cache (eller ej) transparent og returnere den nye værdi.

Nogle gange er det nyttigt at bruge en “AND” til at formidle hensigter som med Java-metoderne getAndIncrement og incrementAndGet at “s bestemt ikke er fra bordet.

Svar

Du siger, at funktionen returnerer noget” fra en transformation, ikke fra enhver db-handling “.

Dette antyder, at funktionen opfører sig mere som en konstruktør end en getter. -konstruktører bør dog ikke forårsage bivirkninger (tak for linket, @ MechMK1 ).

Fabriksmetoder forudsætter derimod allerede noget rod . For eksempel er en af motivationen til at bruge fabriksmetoder, at en fabriksmetode:

Giver mulighed for mere læsbar kode i tilfælde, hvor der findes flere konstruktører, hver til en anden grund. – wikipedia

Ligeledes

Hvis du har brug for noget arbejde med bivirkninger, kan en fabriksmetode navngives på en sådan måde, at det er mere klart, hvad disse bivirkninger er. – ruakh

Overvej at gøre funktionen til en fabriksmetode ved hjælp af udtrykket “logget” for at advare programmører til databaselagring:

  • Erklæring: createLoggedResponseid(...) {...} // log object to db
  • Opkald: responseid = createLoggedResponseid(...);

Kommentarer

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *