I dag skal vi sette på våre datavitenskapshatter når vi lærer om noen vanlige designmønstre. Designmønstre tilbyr utviklere måter å løse tekniske problemer på en gjenbrukbar og elegant måte. Interessert i å bli en bedre JavaScript-utvikler? Deretter les videre.
Publisert opplæringNoen få uker besøker vi noen av leserens favorittinnlegg fra hele historien til nettstedet. Denne opplæringen ble først publisert i juli 2012.
Faste designmønstre er den grunnleggende byggestenen for vedlikeholdbare applikasjoner. Hvis du noen gang har deltatt i et teknisk intervju, har du likt å bli spurt om dem. I denne opplæringen tar vi en titt på noen få mønstre som du kan begynne å bruke i dag.
Et designmønster er en gjenbrukbar programvareløsning
Enkelt sagt, et designmønster er en gjenbrukbar programvareløsning til en bestemt type problem som ofte oppstår når du utvikler programvare. I løpet av de mange årene med å praktisere programvareutvikling, har eksperter funnet ut måter å løse lignende problemer. Disse løsningene er innkapslet i designmønstre. Så:
Vi kommer inn på noen eksempler på designmønstre videre i opplæringen.
I programvareutvikling er designmønstre generelt gruppert i noen få kategorier. Vi dekker de tre viktigste i denne opplæringen. De er forklart kort nedenfor:
Du kan fortsatt ha spørsmål etter å ha lest disse korte beskrivelsene. Dette er naturlig, og ting vil klare seg når vi ser på noen designmønstre i dybden nedenfor. Så les videre!
Når du leser om designmønstre, ser du ofte referanser til klasser og objekter. Dette kan være forvirrende, siden JavaScript ikke egentlig har konstruksjonen av "klasse"; et mer riktig uttrykk er "datatype".
JavaScript er et objektorientert språk der objekter arver fra andre objekter i et konsept kjent som prototypisk arv. En datatype kan opprettes ved å definere hva som kalles en konstruktørfunksjon, slik:
funksjon Person (config) this.name = config.name; this.age = config.age; Person.prototype.getAge = function () return this.age; ; var tilo = ny person (navn: "Tilo", alder: 23); console.log (tilo.getAge ());
Legg merke til bruken av prototype
når du definerer metoder på Person
data-type. Siden flere Person
objekter vil referere til samme prototype, dette tillater getAge ()
Metode som skal deles av alle forekomster av Person
datatype, snarere enn å omdefinere den for hver forekomst. I tillegg er enhver datatype som arver fra Person
vil ha tilgang til getAge ()
metode.
Et annet vanlig problem i JavaScript er at det ikke er noen sans for private variabler. Imidlertid kan vi bruke nedleggelser til å noe simulere personvern. Vurder følgende utdrag:
var retinaMacbook = (funksjon () // Private variabler var RAM, addRAM; RAM = 4; // Privat metode addRAM = funksjon (additionalRAM) RAM + = additionalRAM;; return // Offentlige variabler og metoder USB: undefined , insertUSB: funksjon (enhet) this.USB = device;, removeUSB: function () var enhet = this.USB; this.USB = undefined; return device;;) ();
I eksemplet ovenfor opprettet vi en retinaMacbook
objekt, med offentlige og private variabler og metoder. Slik bruker vi det:
retinaMacbook.insertUSB ( "myUSB"); console.log (retinaMacbook.USB); // logger ut "myUSB" console.log (retinaMacbook.RAM) // logger ut undefined
Det er mye mer som vi kan gjøre med funksjoner og nedleggelser i JavaScript, men vi kommer ikke inn i alt i denne opplæringen. Med denne lille leksjonen på JavaScript datatyper og personvern bak oss, kan vi fortsette å lære om designmønstre.
Det er mange forskjellige typer kreative designmønstre, men vi skal dekke to av dem i denne opplæringen: Builder and Prototype. Jeg finner at disse brukes ofte nok til å berettige oppmerksomheten.
Builder Pattern er ofte brukt i webutvikling, og du har sikkert brukt den før uten å innse det. Enkelt sagt, dette mønsteret kan defineres som følgende:
Ved å bruke byggemønsteret kan vi lage objekter ved bare å spesifisere typen og innholdet til objektet. Vi trenger ikke å opprette objektet eksplisitt.
For eksempel har du sannsynligvis gjort dette utallige ganger i jQuery:
var myDiv = $ ('Dette er en div.'); // myDiv representerer nå et jQuery-objekt som refererer til en DOM-node. var someText = $ (''); // someText er et jQuery-objekt som refererer til en HTMLParagraphElement var input = $ ('');
Ta en titt på de tre eksemplene ovenfor. I den første passerte vi i a element med noe innhold. I det andre passerte vi tomt
stikkord. I det siste passerte vi i en
element. Resultatet av alle tre var det samme: Vi ble returnert et jQuery-objekt som refererer til en DOM-node.
De $
variabel vedtar byggemønsteret i jQuery. I hvert eksempel ble vi returnert et jQuery DOM-objekt og hadde tilgang til alle metodene som ble levert av jQuery-biblioteket, men på ingen måte ringte vi eksplisitt document.createElement
. JS-biblioteket håndterte alt dette under hetten.
Tenk deg hvor mye arbeid det ville være hvis vi måtte eksplisitt lage DOM-elementet og sette inn innhold i det! Ved å utnytte byggemønsteret kan vi fokusere på typen og innholdet til objektet, i stedet for eksplisitt opprettelse av det.
Tidligere gikk vi gjennom hvordan du definerer datatyper i JavaScript gjennom funksjoner og legger til metoder for objektets prototype
. Prototypemønsteret lar objekter arve fra andre gjenstander, via deres prototyper.
Prototypemønsteret er et mønster der objekter blir opprettet basert på en mal av et eksisterende objekt gjennom kloning.
Dette er en enkel og naturlig måte å implementere arv på i JavaScript. For eksempel:
var Person = numFeet: 2, numHeads: 1, numHands: 2; //Object.create tar sitt første argument og bruker det til prototypen til det nye objektet. var tilo = Object.create (Person); console.log (tilo.numHeads); // utganger 1 tilo.numHeads = 2; console.log (tilo.numHeads) // utganger 2
Egenskapene (og metodene) i Person
objekt blir brukt på prototypen av tilo
gjenstand. Vi kan omdefinere egenskapene på tilo
objekt hvis vi vil at de skal være forskjellige.
I eksemplet ovenfor brukte vi Object.create ()
. Imidlertid støtter Internet Explorer 8 ikke den nyere metoden. I disse tilfellene kan vi simulere sin oppførsel:
var vehiclePrototype = init: funksjon (carModel) this.model = carModel; , getModel: funksjon () console.log ("Modellen til dette kjøretøyet er" + this.model); ; funksjonsbil (modell) funksjon F () ; F. prototype = vehikelprototype; var f = ny F (); f.init (modell); returnere f; varbil = kjøretøy ("Ford Escort"); car.getModel ();
Den eneste ulempen med denne metoden er at du ikke kan spesifisere skrivebeskyttede egenskaper, som kan angis når du bruker Object.create ()
. Ikke desto mindre viser prototypemønsteret hvordan gjenstander kan arve fra andre objekter.
Strukturelle designmønstre er veldig nyttige når du skal finne ut hvordan et system skal fungere. De tillater at våre applikasjoner skaleres enkelt og forbli vedlikeholdsdyktig. Vi skal se på følgende mønstre i denne gruppen: Kompositt og fasade.
Komposittmønsteret er et annet mønster som du sannsynligvis har brukt før uten noen realisering.
Komposittmønsteret sier at en gruppe objekter kan behandles på samme måte som et enkelt objekt fra gruppen.
Så hva betyr dette? Vel, betrakt dette eksemplet i jQuery (de fleste JS-biblioteker vil ha tilsvarende til dette):
$ ( 'MyList ') addClass (' valgt.'); . $ ( '# MyItem') addClass ( 'valgt'); // gjør ikke dette på store bord, det er bare et eksempel. $ ("# dataTable tbody tr"). på ("klikk", funksjon (hendelse) alarm ($ (dette) .text ());); $ ('# myButton'). på ("klikk", funksjon (hendelse) alert ("Clicked.";;);
De fleste JavaScript-biblioteker gir en konsekvent API, uavhengig av om vi har et enkelt DOM-element eller en rekke DOM-elementer. I det første eksemplet kan vi legge til valgt
klasse til alle elementene hentet av .min liste
velg, men vi kan bruke samme metode når vi arbeider med et entydig DOM-element, #myItem
. På samme måte kan vi legge ved hendelseshåndterere ved hjelp av på()
metode på flere noder, eller på en enkelt node gjennom samme API.
Ved å utnytte komposittmønsteret gir jQuery (og mange andre biblioteker) oss en forenklet API.
Komposittmønsteret kan noen ganger også forårsake problemer. I et løst skrevet språk som JavaScript kan det ofte være nyttig å vite om vi har et enkelt element eller flere elementer. Siden komposittmønsteret bruker samme API for begge, kan vi ofte misforstå den ene til den andre og ende opp med uventede feil. Noen libarier, som YUI3, tilbyr to separate metoder for å få elementer (Y.one ()
vs Y.all ()
).
Her er et annet vanlig mønster som vi tar for gitt. Faktisk er denne en av mine favoritter fordi den er enkel, og jeg har sett den brukt over alt for å hjelpe til med nettleserens inkonsekvenser. Her er det fasade mønsteret handler om:
Fasadmønsteret gir brukeren et enkelt grensesnitt, samtidig som den skjuler den underliggende kompleksiteten.
Facade-mønsteret forbedrer nesten alltid brukbarheten til et program. Bruk av jQuery som et eksempel igjen, er en av de mest populære metodene i biblioteket klar()
metode:
$ (dokument) .ready (funksjon () // all koden din går her ...);
De klar()
Metoden implementerer faktisk en fasade. Hvis du ser på kilden, er det hva du finner:
klar: (funksjon () ... // Mozilla, Opera og Webkit hvis (document.addEventListener) document.addEventListener ("DOMContentLoaded", idempotent_fn, false); ... // IE hendelsesmodell annet hvis (document.attachEvent) // sørg for å skyte før lastet, kanskje sent, men også trygt for iframes document.attachEvent ("onreadystatechange", idempotent_fn); // Et fallback til window.onload, det vil alltid fungere window.attachEvent ("onload", idempotent_fn); ...)
Under hetten, den klar()
Metoden er ikke så enkelt. jQuery normaliserer nettleserens uoverensstemmelser for å sikre det klar()
blir sparket på riktig tidspunkt. Men som utvikler presenteres du med et enkelt grensesnitt.
De fleste eksempler på fasade mønsteret følger dette prinsippet. Når vi implementerer en, stole vi vanligvis på betingede utsagn under hetten, men presenterer det som et enkelt grensesnitt for brukeren. Andre metoder som implementerer dette mønsteret inkluderer animere ()
og css ()
. Kan du tenke på hvorfor disse ville bruke et fasade mønster?
Eventuelle objektorienterte programvare systemer vil ha kommunikasjon mellom objekter. Ikke å organisere kommunikasjonen kan føre til feil som er vanskelig å finne og fikse. Behavioral design mønstre foreskrive ulike metoder for å organisere kommunikasjonen mellom objekter. I denne delen skal vi se på Observer og Mediator-mønstrene.
Observer-mønsteret er det første av de to atferdsmønstrene vi skal gjennomgå. Her er hva det står:
I observatørmønsteret kan et emne ha en liste over observatører som er interessert i sin livssyklus. Når som helst motivet gjør noe interessant, sender det et varsel til observatørene. Hvis en observatør ikke lenger er interessert i å lytte til emnet, kan emnet fjerne det fra listen.
Høres ganske enkelt, ikke sant? Vi trenger tre metoder for å beskrive dette mønsteret:
publiserer (data)
: Kalt av emnet når det er et varsel om å gjøre. Enkelte data kan bestå av denne metoden. abonnere (observatør)
: Oppfordret til å legge til en observatør i listen over observatører. unsubscribe (observatør)
: Kalt av emnet for å fjerne en observatør fra listen over observatører. Vel, det viser seg at de fleste moderne JavaScript-biblioteker støtter disse tre metodene som en del av sin tilpassede hendelsesinfrastruktur. Vanligvis er det en på()
eller feste()
metode, a avtrekker()
eller Brann()
metode, og en av()
eller løsne()
metode. Vurder følgende utdrag:
// Vi oppretter bare en tilknytning mellom jQuery-hendelsesmetodene
// og de som er foreskrevet av observatørmønsteret, men du trenger ikke. var o = $ (); $ .subscribe = o.on.bind (o); $ .unsubscribe = o.off.bind (o); $ .publish = o.trigger.bind (o); // Bruk document.on ('tweetsReceived', funksjon (tweets) // utfør noen handlinger, og brann en event $ .publish ('tweetsShow', tweets);); // Vi kan abonnere på denne hendelsen og så brenne vår egen begivenhet. $ .subscribe ('tweetsShow', funksjon () // vise tweets på en eller annen måte ... // publiser en handling etter at de vises. $ .publish ('tweetsDisplayed);); $ .subscribe ('tweetsDisplayed, function () ...);
Observer-mønsteret er et av de enkleste mønstrene å implementere, men det er veldig kraftig. JavaScript er godt egnet til å adoptere dette mønsteret som det er naturlig hendelsesbasert. Neste gang du utvikler webapplikasjoner, tenk å utvikle moduler som er løst koblet til hverandre og vedta Observer-mønsteret som kommunikasjonsmiddel. Observatørmønsteret kan bli problematisk hvis det er for mange fag og observatører involvert. Dette kan skje i store systemer, og det neste mønsteret vi ser på, prøver å løse dette problemet.
Det siste mønsteret vi skal se på er Mediator Pattern. Det ligner Observer-mønsteret, men med noen bemerkelsesverdige forskjeller.
Mediatormønsteret fremmer bruk av et enkelt delt emne som håndterer kommunikasjon med flere objekter. Alle objekter kommuniserer med hverandre gjennom mekleren.
En god virkelighet analogi ville være et lufttrafik tårn, som håndterer kommunikasjon mellom flyplassen og flyene. I programvareutviklingsverdenen brukes mediatormønsteret ofte som et system blir altfor komplisert. Ved å plassere mediatorer kan kommunikasjon håndteres gjennom et enkelt objekt, i stedet for å ha flere objekter som kommuniserer med hverandre. I denne forstand kan et mediator mønster brukes til å erstatte et system som utfører observatørmønsteret.
Det er en forenklet implementering av mediatormønsteret av Addy Osmani i dette kjernen. La oss snakke om hvordan du kan bruke den. Tenk deg at du har en webapp som lar brukerne klikke på et album og spille av musikk fra den. Du kan sette opp en mediator som dette:
$ ('# album'). På ('klikk', funksjon (e) e.preventDefault (); var albumId = $ (dette) .id (); mediator.publish ("playAlbum", albumId);) ; var playAlbum = funksjon (id) ... mediator.publish ("albumStartedPlaying", sangliste: [...], nåværendeSong: "Uten deg"); ; var logAlbumPlayed = funksjon (id) // Logg inn albumet i baksiden; var updateUserInterface = funksjon (album) // Oppdater brukergrensesnitt for å gjenspeile det som spilles av; // Mediatorabonnementer mediator.subscribe ("playAlbum", playAlbum); mediator.subscribe ("playAlbum", logAlbumPlayed); mediator.subscribe ("albumStartedPlaying", updateUserInterface);
Fordelen med dette mønsteret over Observer-mønsteret er at et enkelt objekt er ansvarlig for kommunikasjon, mens i observatørmønsteret kan flere gjenstander lytte og abonnere på hverandre.
I Observer-mønsteret finnes det ingen enkelt objekt som innkapsler en begrensning. I stedet må Observer og Emne samarbeide for å opprettholde begrensningen. Kommunikasjonsmønstre er bestemt av måten observatører og fag er sammenkoblet til: Et enkelt emne har vanligvis mange observatører, og noen ganger er observatøren av ett emne gjenstand for en annen observatør.
Noen har allerede brukt det med hell tidligere.
Det flotte med designmønstre er at noen allerede har brukt det med hell i fortiden. Det er mange åpen kildekode som implementerer ulike mønstre i JavaScript. Som utviklere må vi være oppmerksomme på hvilke mønstre som er der ute og når de skal bruke dem. Jeg håper denne opplæringen hjalp deg med å ta ett skritt videre mot å svare på disse spørsmålene.
Mye av innholdet fra denne artikkelen finner du i den utmerkede læreboken for JavaScript Design Patterns, av Addy Osmani. Det er en online-bok som ble utgitt gratis under Creative Commons-lisens. Boken dekker i stor grad teorien og implementeringen av mange forskjellige mønstre, både i vanilla JavaScript og forskjellige JS-biblioteker. Jeg oppfordrer deg til å se på det som en referanse når du starter ditt neste prosjekt.