def spectre_member_name

Anti-hva? Det høres sannsynligvis mye mer komplisert enn det er. I løpet av de siste par tiårene var programmører i stand til å identifisere et nyttig utvalg av "design" -mønstre som ofte skjedde gjennom deres kodeløsninger. Mens de løste lignende problemer, var de i stand til å "klassifisere" løsninger som forhindret dem i å gjenoppfinne hjulet hele tiden. Det er viktig å merke seg at disse mønstrene skal ses mer som funn enn oppfinnelsen til en gruppe av avanserte utviklere.

Hvis dette er ganske nytt for deg og du ser deg selv som å være mer på begynnelsersiden av alle ting Ruby / Rails, så er denne nøyaktig skrevet for deg. Jeg synes det er best hvis du tenker på det som en rask, tynn dukkert i et mye dypere emne hvis mestring ikke skjer over natten. Likevel tror jeg sterkt at begynnelsen for å komme inn i denne tidlige viljen nyter nybegynnere og deres mentorer enormt.

AntiPatterner - som navnet antyder - representerer ganske mye det motsatte av mønstre. De er funn av løsninger på problemer som du definitivt bør unngå. De representerer ofte arbeidet med uerfarne kodere som ikke vet hva de ennå ikke vet. Verre, de kan være produksjonen av en lat person som bare ignorerer beste praksis og verktøyrammer uten god grunn - eller de tror de ikke trenger dem. Det de kanskje håper å få i tidsbesparelser i begynnelsen ved å hamre ut raske, dovne eller skitne løsninger, kommer til å hjemsøke dem eller noen beklager etterfølger senere i prosjektets livssyklus.

Ikke undervurder implikasjonene til disse dårlige beslutningene - de kommer til å plage deg, uansett hva.

emner

  • Fettmodeller
  • Mangler Test Suite
  • Voyeuristic Modeller
  • Demeter lov
  • Spaghetti SQL

Fettmodeller

Jeg er sikker på at du hørte "Fat models, tynne controllers" syngesangen mange ganger da du først startet med Rails. OK, glem det nå! Visst, forretningslogikken må løses i modelllaget, men du bør ikke føle seg tilbøyelig til å kaste alt der inne meningsløst bare for å unngå å krysse linjene inn i kontrollerområdet.

Her er et nytt mål du bør sikte på: "Skinny modeller, tynne kontrollere". Du kan spørre, "Vel, hvordan skal vi ordne kode for å oppnå det? Tross alt er det et null-sumspill?" Bra poeng! Navnet på spillet er sammensetning, og Ruby er godt rustet til å gi deg mange muligheter for å unngå modell fedme.

I de fleste (Rails) webapplikasjoner som støttes av databasen, vil mesteparten av din oppmerksomhet og arbeid være sentrert rundt modelllaget - gitt at du jobber med kompetente designere som kan implementere sine egne ting i visningen, mener jeg. Modellene dine vil iboende ha mer "tyngdekraften" og tiltrekke seg mer kompleksitet.

Spørsmålet er bare hvordan du har tenkt å håndtere den kompleksiteten. Active Record gir deg absolutt massevis av tau for å henge deg selv mens du gjør livet ditt utrolig enkelt. Det er en fristende tilnærming til å designe modelllaget ved bare å følge banen med høyest umiddelbar bekvemmelighet. Ikke desto mindre tar en fremtidssikker arkitektur mye mer hensyn enn å dyrke store klasser og fylle alt i Active Record-objekter.

Det virkelige problemet som du håndterer her er kompleksitet - unødvendig, så vil jeg si. Klasser som samler tonn kode blir komplekse bare etter deres størrelse alene. De er vanskeligere å vedlikeholde, vanskelig å analysere og forstå, og stadig vanskeligere å endre fordi deres sammensetning sannsynligvis mangler avkobling. Disse modellene overgår ofte deres anbefalte kapasitet til å håndtere et enkelt ansvar og er ganske over alt. Verste fallet, de blir som søppelbusser, og håndterer alt søppel som er sløyfe kastet på dem.

Vi kan gjøre det bedre! Hvis du mener at kompleksiteten ikke er en stor avtale, er du allikevel spesiell, smart og all-think igjen! Kompleksitet er den mest beryktede serielle prosjektmordet der ute - ikke ditt vennlige nabolag "Dark Defender".

"Skinnier-modeller" oppnår en ting avanserte folk i kodingsvirksomheten (sannsynligvis mange flere yrker enn kode og design) setter pris på og hva vi alle bør absolutt strebe for-enkelhet! Eller i det minste mer av det, noe som er et rettferdig kompromiss hvis kompleksiteten er vanskelig å utrydde.

Hvilke verktøy tilbyr Ruby å gjøre livet enklere i den forbindelse og la oss trimme fettet ut av våre modeller? Enkle, andre klasser og moduler. Du identifiserer koherent kode som du kan trekke ut i et annet objekt og derved bygge et modellelag som består av rimelig størrelse agenter som har sitt eget unike, særegne ansvar.

Tenk på det når det gjelder en talentfull artist. I virkeligheten, kan en slik person være i stand til å rape, knuse, skrive tekster og lage egne melodier. I programmering foretrekker du dynamikken til et band - her med minst fire karakteristiske medlemmer - hvor hver person har ansvar for så få ting som mulig. Du vil bygge et orkester av klasser som kan håndtere kompleksiteten til komponisten-ikke en mikromanaging genius maestro klasse av alle handler.

La oss se på et eksempel på en fet modell og spille med et par alternativer for å håndtere fedme. Eksemplet er en dummy en, selvfølgelig, og ved å fortelle denne tunge lille historien håper jeg det blir lettere å fordøye og følge etter nybegynnere.

Vi har en Specter klasse som har for mange ansvar og har derfor vokst unødvendig. Foruten disse metodene, tror jeg det er lett å forestille seg at et slikt eksemplar allerede har samlet mange andre ting som godt representert av de tre små prikkene. Spektre er godt på vei til å bli en gudklasse. (Sjansene er ganske lave til fornuftig formulere en slik setning igjen når som helst snart!)

"Ruby Class Specter < ActiveRecord::Base has_many :spectre_members has_many :spectre_agents has_many :enemy_agents has_many :operations

...

def turn_mi6_agent (enemy_agent) setter "MI6 agent # enemy_agent.name vendt over til Specter" slutten

def turn_cia_agent (enemy_agent) setter "CIA agent # enemy_agent.name vendt over til Specter" slutten

def turn_mossad_agent (enemy_agent) setter "Mossad agent # enemy_agent.name vendt over til Specter" slutten

def kill_double_o_seven (spectre_agent) spectre_agent.kill_james_bond end

def dispose_of_cabinet_member (number) spectre_member = SpectreMember.find_by_id (nummer)

setter "En viss synder har sviktet denne brorskapets absolutte integritet. Den rette handlingen er å røyke nummer # nummer i stolen. Hans tjenester vil ikke bli savnet i stor grad" spectre_member.die end 

def print_assignment (drift) setter "Operation # operation.name 's mål er å # operation.objective." avslutte

privat

def enemy_agent #clever kode ende

def spectre_agent #clever kode ende

def operasjon #clever kode ende

...

slutt

"

Specter vender ulike typer fiendtlige agenter, delegater dreper 007, griller Specters kabinetsmedlemmer når de feiler, og skriver også ut operasjonsoppgaver. Et klart tilfelle av mikromanagement og definitivt et brudd på "Single Responsibility Principle". Private metoder stabler også raskt opp.

Denne klassen trenger ikke å vite de fleste av de ting som er i det. Vi vil dele denne funksjonaliteten i et par klasser og se om kompleksiteten ved å ha et par flere klasser / objekter er verdt fettsuging.

"rubin

klassespekter < ActiveRecord::Base has_many :spectre_members has_many :spectre_agents has_many :enemy_agents has_many :operations

...

def turn_enemy_agent Interrogator.new (enemy_agent) .turn slutten

privat

def enemy_agent self.enemy_agents.last slutten

klasse Interrogator attr_reader: enemy_agent

def initialisere (enemy_agent) @enemy_agent = enemy_agent end

def slå enemy_agent.turn slutten

klassen EnemyAgent < ActiveRecord::Base belongs_to :spectre belongs_to :agency

def turn puts 'Etter omfattende hjernevasking, tortur og hoards of cash ...' slutten

klasse MI6Agent < EnemyAgent def turn super puts “MI6 agent #name turned over to Spectre” end end

klasse CiaAgent < EnemyAgent def turn super puts “CIA agent #name turned over to Spectre” end end

klasse MossadAgent < EnemyAgent def turn super puts “Mossad agent #name turned over to Spectre” end end

klasse NumberOne < ActiveRecord::Base def dispose_of_cabinet_member(number) spectre_member = SpectreMember.find_by_id(number)

setter "En viss skyldige har sviktet denne brorskapets absolutte integritet. Den rette handlingen er å røyke nummer # nummer i stolen. Hans tjenester vil ikke bli savnet i stor grad" spectre_member.die endeenden 

klasse drift < ActiveRecord::Base has_many :spectre_agents belongs_to :spectre

def print_assignment setter "Operasjon # name 's mål er å # objective." End-end

klasse SpectreAgent < ActiveRecord::Base belongs_to :operation belongs_to :spectre

def kill_james_bond setter "Mr. Bond, jeg forventer at du skal dø! "Slutten

klasse SpectreMember < ActiveRecord::Base belongs_to :spectre

def dør setter "Nooo, nei, det var ikke meeeeeeeee! ZCHUNK! "Endeenden

"

Jeg tror den viktigste delen som du bør være oppmerksom på, er hvordan vi brukte en vanlig Ruby-klasse som forhørs å håndtere vendingen av agenter fra ulike byråer. Eksempler på ekte verden kan representere en omformer som for eksempel forvandler et HTML-dokument til en pdf og omvendt. Hvis du ikke trenger full funksjonalitet av Active Record-klasser, hvorfor bruke dem hvis en enkel Ruby-klasse også kan gjøre trikset? Litt mindre tau å henge oss med.

Spektaklassen etterlater den ekle virksomheten til å snu agenter til forhørs klassen og bare delegerer til den. Denne har nå det eneste ansvaret for torturering og hjernevasking som er fanget.

Så langt så bra. Men hvorfor opprettet vi separate klasser for hver agent? Enkel. I stedet for bare å direkte utvinne de ulike svingmetoder som turn_mi6_agent over til forhørs, Vi ga dem et bedre hjem i sin egen respektive klasse.

Som et resultat kan vi effektivt gjøre bruk av polymorfisme og bryr seg ikke om enkelte tilfeller for svingemidlene. Vi forteller bare disse forskjellige agentobjektene å slå, og hver av dem vet hva de skal gjøre. De forhørs trenger ikke å vite detaljene om hvordan hver agent blir.

Siden alle disse agenter er Active Record-objekter, opprettet vi en generisk, EnemyAgent, Det har en generell følelse av hva som betyr å snu en agent, og vi inkapslerer den for alle agenter på ett sted ved å subclassere den. Vi benytter denne arven ved å levere sving metoder for de forskjellige midlene med super, og derfor får vi tilgang til hjernevasking og torturvirksomhet uten duplisering. Enkelt ansvar og ingen duplisering er et godt utgangspunkt for videreføring.

De andre Active Record-klassene tar på seg ulike ansvarsområder som Specter ikke trenger å bry seg om. "Nummer One" pleier vanligvis grilling av mislykkede Specter-kabinettmedlemmer selv, så hvorfor ikke la en dedikert gjenstand håndtere elektrocution? På den annen side vet ikke spektermedlemmer hvordan de skal dø selv når de blir røkt i stolen av Nummer en. Operasjon skriver nå også oppdragene sine selv - det er ikke nødvendig å kaste bort tiden til Specter med peanøtter sånn.

Sist, men ikke minst, blir drap James Bond vanligvis forsøkt av en agent i feltet, så kill_james_bond er nå en metode på SpectreAgent. Goldfinger ville ha håndtert det annerledes, selvfølgelig, må du leke med den laseren thingie hvis du har en, antar jeg.

Som du tydeligvis kan se, har vi nå ti klasser der vi tidligere kun hadde en. Er det ikke for mye? Det kan sikkert være. Det er et problem du må kjempe med mesteparten av tiden når du deler opp slike ansvarsområder. Du kan definitivt overdrive dette. Men å se på dette fra en annen vinkel kan hjelpe:

  • Har vi skilt bekymringer? Absolutt!
  • Har vi lette, tynne klasser som er bedre egnet til å håndtere singulære ansvarsområder? Ganske sikker!
  • Forteller vi en "historie", maler vi et klarere bilde av hvem som er involvert og har ansvaret for visse handlinger? jeg håper det!
  • Er det lettere å fordøye hva hver klasse gjør? Helt sikkert!
  • Har vi kuttet ned antall private metoder? Jepp!
  • Betyr dette en bedre kvalitet av objektorientert programmering? Siden vi brukte komposisjon og bare henviste til arv når det var nødvendig for å sette opp disse objektene, satse du på!
  • Føles det renere? Ja!
  • Er vi bedre rustet til å endre koden vår uten å gjøre et rot? Sikkert!
  • Var det verdt det? Hva tror du?

Jeg antar ikke at disse spørsmålene må kontrolleres av listen hver gang, men det er de tingene du sannsynligvis burde begynne å spørre deg selv mens du slanker ned modellene dine.

Å utforme tynne modeller kan være vanskelig, men det er et viktig tiltak for å holde applikasjonene sunne og smidige. Disse er heller ikke de eneste konstruktive måtene å håndtere fettmodeller, men de er en god start, spesielt for nybegynnere.

Mangler Test Suite

Dette er trolig den mest åpenbare AntiPattern. Å komme fra den testdrevne siden av ting, ved å røre en moden app som ikke har testdekning, kan være en av de mest smertefulle opplevelsene som møter. Hvis du vil hate verden og ditt eget yrke mer enn noe, bare bruke seks måneder på et slikt prosjekt, og du vil lære hvor mye av en misantrop som muligens er i deg. Å tuller, selvfølgelig, men jeg tviler på at det vil gjøre deg lykkeligere og at du vil gjøre det igjen - noensinne. Kanskje en uke også vil gjøre det. Jeg er ganske sikker på at ordet tortur vil dukke opp i tankene dine oftere enn du tror.

Hvis testingen ikke var en del av prosessen så langt, og den slags smerte føles normal for arbeidet ditt, bør du kanskje vurdere at testing ikke er så dårlig, og det er heller ikke din fiende. Når dine kodenelaterte gledenivåer er mer eller mindre stadig over null, og du kan fryktløst endre koden din, vil den generelle kvaliteten på arbeidet ditt være mye høyere i forhold til produksjonen som er besvimt av angst og lidelse.

Overestimerer jeg? Jeg tror egentlig ikke det! Du vil ha en svært omfattende testdekning, ikke bare fordi det er et flott designverktøy for bare å skrive kode som du faktisk trenger, men også fordi du må endre koden din på et tidspunkt i fremtiden. Du vil være mye bedre rustet til å engasjere seg med kodebase-og mye mer selvsikkerhet - hvis du har en test sele som hjelper og veileder refactorings, vedlikehold og utvidelser. De vil oppstå sikkert nedover veien, null tviler på det.

Dette er også poenget hvor en testpakke begynner å betale ut den andre runden av utbytte, fordi den økte hastigheten som du sikkert kan gjøre disse kvalitetsendringene ikke kan oppnås med en lang skudd i apper som er laget av folk som tenker å skrive tester er tull eller tar for mye tid.

Voyeuristic Modeller

Dette er modeller som er super nysgjerrige og ønsker å samle for mye informasjon om andre objekter eller modeller. Det er i sterk kontrast til en av de mest grunnleggende ideene i Objektorientert Programmering-innkapsling. Vi vil heller streve for selvstendige klasser og modeller som styrer sine interne forhold så mye som mulig. Når det gjelder programmeringskonsepter, bryter disse voyeuristiske modellene i utgangspunktet "Princip of Minimum Knowledge", aka "Demeter Law" - uansett hvor du vil uttale det.

Demeter lov

Hvorfor er dette et problem? Det er en form for duplisering - en subtil en - og fører også til kode som er mye mer sprø enn forventet.

Demeterloven er ganske mye den mest pålitelige kode lukten som du alltid kan angripe uten å være bekymret for mulige ulemper.

Jeg antar å ringe denne en "lov" var ikke så pretensiøs som det kanskje høres først. Grav inn i denne lukten, fordi du trenger det mye i prosjektene dine. Det står i utgangspunktet at når det gjelder objekter, kan du ringe metoder på objektets venn, men ikke på vennens venn.

Dette er en vanlig måte å forklare det på, og det kaster seg ned til å bruke ikke mer enn en enkelt prikk for metallsamtaler. Forresten, det er helt fint å bruke flere prikker eller metallsamtaler når du håndterer et enkelt objekt som ikke forsøker å nå lenger enn det. Noe som @ weapons.find_by_name ('Poison dart'). formel Det er helt fint. Finders kan hakke opp ganske mange prikker noen ganger. Encapsulating dem i dedikerte metoder er likevel en god ide.

Demeterlovenes lov

La oss se på noen få dårlige eksempler fra klassene ovenfor:

"rubin

@ operation.spectre_agents.first.kill_james_bond

@ spectre.operations.last.spectre_agents.first.name

@ spectre.enemy_agents.last.agency.name

"

For å få tak i det, er det noen få fiktive:

"rubin

@ quartermaster.gizmos.non_lethal.favorite

@ mi6.operation.agent.favorite_weapon

@ mission.agent.name

"

Bananer, ikke sant? Ser ikke bra ut, gjør det? Som du ser, kaller disse metodene for mye inn i virksomheten til andre objekter. Den viktigste og åpenbare negative konsekvensen er å forandre en haug med disse metodene ringer over alt om strukturen til disse objektene må endres, hvilket de til slutt vil, fordi den eneste konstanten i programvareutvikling er endring. Også, det ser veldig stygg, ikke lett på øynene i det hele tatt. Når du ikke vet at dette er en problematisk tilnærming, lar du Rails å ta dette veldig langt uansett - uten å skrike på deg. Mange tau, husk?

Så hva kan vi gjøre med dette? Tross alt vil vi få den informasjonen på en eller annen måte. For en ting kan vi komponere våre objekter slik at de passer til våre behov, og vi kan gjøre smart bruk av delegasjonen for å holde våre modeller slanke samtidig. La oss dykke inn i en kode for å vise deg hva jeg mener.

"rubin

klasse SpectreMember < ActiveRecord::Base has_many :operations has_many :spectre_agents

...

slutt

klasse drift < ActiveRecord::Base belongs_to :spectre_member

...

slutt

klasse SpectreAgent < ActiveRecord::Base belongs_to :spectre_member

...

slutt

@ spectre_member.spectre_agents.all @ spectre_member.operations.last.print_assignment @ spectre_member.spectre_agents.find_by_id (1) .name

@ drift.spectre_member.name @ operation.spectre_member.number @ operation.spectre_member.spectre_agents.first.name

@ spectre_agent.spectre_member.number

"

"rubin

klasse SpectreMember < ActiveRecord::Base has_many :operations has_many :spectre_agents

...

def list_of_agents spectre_agents.all end

def print_operation_details operation = Operation.last operation.print_operation_details sluttend

klasse drift < ActiveRecord::Base belongs_to :spectre_member

...

def spectre_member_name spectre_member.name end

def spectre_member_number spectre_member.number slutten

def print_operation_details setter "Denne operasjonens mål er # objective. Målet er # target "slutten

klasse SpectreAgent < ActiveRecord::Base belongs_to :spectre_member

...

def superior_in_charge legger "Min sjef er nummer # spectre_member.number" slutten

@ spectre_member.list_of_agents @ spectre_member.print_operation_details

@ drift.spectre_member_name @ operation.spectre_member_number

@ spectre_agent.superior_in_charge

"

Dette er definitivt et skritt i riktig retning. Som du ser, pakket vi inn informasjonen vi ønsket å skaffe oss i en mengde wrapper-metoder. I stedet for å nå direkte over mange objekter, abstrakte vi disse broene og la den til de respektive modellene for å snakke med vennene deres om den informasjonen vi trenger.

Ulempen med denne tilnærmingen er å ha alle disse ekstra innpakningsmetoder som ligger rundt. Noen ganger er det bra, men vi vil virkelig unngå å opprettholde disse metodene i en rekke steder hvis en gjenstand endres.

Om mulig er det dedikerte stedet for dem å forandre seg på deres gjenstand - og bare på deres gjenstand. Forurensende gjenstander med metoder som har lite å gjøre med sin egen modell, er også noe å passe på siden dette alltid er en potensiell fare for vanning ned enkelt ansvar.

Vi kan gjøre det bedre enn det. Når det er mulig, la oss delegere metallsamtaler direkte til sine objekter som er ansvarlige, og prøv å kutte ned på wrapper-metoder så mye som mulig. Rails vet hva vi trenger og gir oss det praktiske delegat klassemetode for å fortelle vennens venner hvilke metoder vi trenger kalt.

La oss zoome inn på noe fra forrige kodeeksempel og se hvor vi kan gjøre riktig bruk av delegasjonen.

"rubin

klasse drift < ActiveRecord::Base belongs_to :spectre_member

delegere: navn,: nummer, til:: spectre_member, prefiks: true

...

def spectre_member_name

# spectre_member.name # end

def spectre_member_number

# spectre_member.number # end

...

slutt

@ drift.spectre_member_name @ operation.spectre_member_number

klasse SpectreAgent < ActiveRecord::Base belongs_to :spectre_member

delegere: nummer, til:: spectre_member, prefix: true

...

def superior_in_charge legger "Min sjef er nummer # spectre_member_number" slutten

...

slutt

"

Som du kan se, kan vi forenkle ting litt ved hjelp av metodegruppering. Vi ble kvitt Operasjon # spectre_member_name og Operasjon # spectre_member_number helt, og SpectreAgent trenger ikke å ringe Nummerspectre_member lenger-Nummer er delegert tilbake direkte til sin "opprinnelses" klasse SpectreMember.

I tilfelle dette er litt forvirrende først, hvordan fungerer dette nøyaktig? Du forteller delegat hvilken : method_name det bør delegere til: hvilken :klassenavn (flere metodenavn er også bra). De prefiks: sant del er valgfritt.

I vårt tilfelle prefikserte det slangekasset klassenavnet til mottakerklassen før metodenavnet og gjorde det mulig for oss å ringe operation.spectre_member_name i stedet for det potensielt tvetydige operation.name-hvis vi ikke hadde brukt prefiks-alternativet. Dette fungerer veldig bra med tilhører og has_one foreninger.

har mange siden av ting, men musikken vil stoppe og du kommer til å trenge inn i problemer. Disse foreningene gir deg en samlingsproxy som vil kaste NameErrors eller NoMethodErrors hos deg når du delegerer metoder til disse "samlingene".

Spaghetti SQL

For å avrunde dette kapittelet om modell AntiPatterns i Rails, vil jeg gjerne bruke litt tid på hva jeg skal unngå når SQL er involvert. Active Record-foreninger gir alternativer som gjør livet ditt betydelig lettere når du er klar over hva du bør holde seg borte fra. Finder-metoder er et helt tema for seg selv, og vi vil ikke dekke dem i full dybde, men jeg vil nevne noen få vanlige teknikker som hjelper deg selv når du skriver veldig enkle.

Ting som vi burde være bekymret for ekko det meste av det vi har lært så langt. Vi vil ha hensikt-avslørende, enkle og rimelig navngitte metoder for å finne ting i våre modeller. La oss dykke rett inn i koden.

"rubin

klasse drift < ActiveRecord::Base

har mange: agenter

...

slutt

klasse agent < ActiveRecord::Base

belong_to: drift

...

slutt

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @agents = Agent.where (operation_id: @ operation.id, licence_to_kill: true) slutten

"

Ser ufarlig ut, nei? Vi leter bare etter en mengde agenter som har lisensen til å drepe for vår ops side. Tenk igjen. Hvorfor skal OperationsController grave inn i internals of Middel? Også er dette virkelig det beste vi kan gjøre for å inkapslere en finner på Middel?

Hvis du tenker at du kan legge til en klassemetode som Agent.find_licence_to_kill_agents som encapsulates finder logikken, er du definitivt et skritt i riktig retning - ikke nesten nok, skjønt.

"rubin

klasse agent < ActiveRecord::Base

belong_to: drift

def self.find_licence_to_kill_agents (operasjon) hvor (operation_id: operation.id, license_to_kill: true) end ...

slutt

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @agents = Agent.find_licence_to_kill_agents (@operation) slutten

"

Vi må være litt mer engasjert enn det. Først og fremst bruker dette ikke foreningene til vår fordel, og innkapsling er også suboptimal. Foreninger som har mange kom med den fordelen vi kan legge til på proxy-arrayet som vi kommer tilbake. Vi kunne ha gjort dette i stedet:

"rubin

klasse drift < ActiveRecord::Base

har mange: agenter

def find_licence_to_kill_agents self.agents.where (lisence_to_kill: true) avslutte ...

slutt

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @agents = @ drift.find_licence_to_kill_agents endeend

"

Dette fungerer sikkert, men er også bare et lite skritt i riktig retning. Ja, kontrolleren er litt bedre, og vi benytter godt av modellforeninger, men du bør fortsatt være mistenksom på hvorfor Operasjon er opptatt av gjennomføringen av å finne en bestemt type Middel. Dette ansvaret hører tilbake til Middel modellen selv.

Navngitte scopes kommer ganske bra med det. Omfangene definerer kjedelig-veldig viktige klassemetoder for modellene dine og gir deg dermed mulighet til å angi nyttige spørringer som du kan bruke som ekstra metallsamtaler på toppen av modellforeningene dine. Følgende to tilnærminger for scoping Middel er likegyldig.

"rubin

klasse agent < ActiveRecord::Base belongs_to :operation

omfang: lisenced_to_kill, -> where (licence_to_kill: true) avslutte

klasse agent < ActiveRecord::Base belongs_to :operation

def self.licenced_to_kill hvor (licence_to_kill: true) slutten

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @agents = @ operation.agents.licenced_to_kill endeend

"

Det er mye bedre. I tilfelle syntaxen av scopes er ny for deg, er de bare (stabby) lambdas-ikke veldig viktig å se på dem med en gang, forresten - og de er den riktige måten å ringe på siden siden Rails 4. Middel er nå ansvarlig for å administrere sine egne søkeparametere, og foreninger kan bare kaste seg på det de trenger å finne.

Denne tilnærmingen lar deg oppnå spørsmål som enkle SQL-anrop. Jeg personlig liker å bruke omfang for dens eksplisitte. Scopes er også veldig nyttige å kjede inne i velkjente søkemetoder, slik at de øker muligheten for å gjenbruke kode og DRY-ing-kode. La oss si at vi har noe litt mer involvert:

"rubin

klasse agent < ActiveRecord::Base belongs_to :operation

omfang: licenced_to_kill, -> hvor (lisens_to_kill: true) omfang: womanizer, -> where (womanizer: true) omfang: bond, -> hvor (navn: 'James Bond') omfang: gambler > hvor (gambler: true) slutten

"

Vi kan nå bruke alle disse omfangene til å skreddersy bygge mer komplekse søk.

"rubin

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @double_o_agents = @ operation.agents.licenced_to_kill end

def show @operation = Operation.find (params [: id]) @bond = @ operation.agents.womanizer.gambler.licenced_to_kill end

… slutt

"

Jo, det fungerer, men jeg vil gjerne foreslå at du går et skritt videre.

"rubin

klasse agent < ActiveRecord::Base belongs_to :operation

omfang: licenced_to_kill, -> hvor (lisens_to_kill: true) omfang: womanizer, -> where (womanizer: true) omfang: bond, -> hvor (navn: 'James Bond') omfang: gambler > hvor (gambler: true)

def self.find_licenced_to_kill lisenced_to_kill end

def self.find_licenced_to_kill_womanizer womanizer.licenced_to_kill end

def self.find_gambling_womanizer gambler.womanizer slutten

...

slutt

klasse OperationsController < ApplicationController

def index @operation = Operation.find (params [: id]) @double_o_agents = @ operation.agents.find_licenced_to_kill end

def show @operation = Operation.find (params [: id]) @bond = @ operation.agents.find_licenced_to_kill_womanizer #or @bond = @ operation.agents.bond end

...

slutt

"

Som du ser, øker vi fordelene ved riktig innkapsling, modellforeninger, kodeutnyttelse og uttrykksfull navngivning av metoder, og samtidig gjør det enkelt SQL-spørringer. Ikke mer spaghetti kode, fantastisk!

Hvis du er bekymret for å bryte loven om Demeter thingie, vil du være glad for å høre at siden vi ikke legger til punkter ved å komme inn i den tilknyttede modellen, men bare knytte dem til eget objekt, begår vi ikke Demeter forbrytelser.

Siste tanker

Fra et nybegynnerperspektiv tror jeg at du har lært mye om bedre håndtering av Rails Models, og hvordan du modellerer dem robustere uten å ringe etter en hangman.

Ikke la deg lure, men tenk at det ikke er mye mer å lære om dette emnet. Jeg presenterte deg med noen få AntiPatterner som jeg tror nybegynnere kan forstå og håndtere for å beskytte seg selv tidlig. Hvis du ikke vet hva du ikke vet, er det nok av tau tilgjengelig for looping rundt halsen.

Selv om dette var en solid start i dette emnet, er det ikke bare flere aspekter til AntiPatterns i Rails-modeller, men også mer nyanser som du må utforske også. Disse var de grunnleggende - svært viktige og viktige - og du burde føle deg oppnådd i en liten stund at du ikke har ventet til mye senere i din karriere for å finne ut dem.