Înțelegerea modelelor de design în JavaScript

Astăzi, vom pune pe palarii știința calculatoarelor, în timp ce învățăm despre unele modele de design obișnuite. Modelele de design oferă dezvoltatorilor modalități de a rezolva problemele tehnice într-un mod reutilizabil și elegant. Vrei să devii un dezvoltator mai bun pentru JavaScript? Apoi citiți mai departe.

Tutorial publicat

La fiecare câteva săptămâni, revizuim câteva postări preferate ale cititorului nostru de-a lungul istoriei site-ului. Acest tutorial a fost publicat pentru prima oară în iulie 2012.


Introducere

Design-urile solide sunt blocul de bază pentru aplicațiile software mentenabile. Dacă ați participat vreodată la un interviu tehnic, v-ați plăcut să fiți întrebat despre ele. În acest tutorial, vom examina câteva modele pe care le puteți începe astăzi.


Ce este un model de design?

Un model de design este o soluție software reutilizabilă

Pur și simplu, un model de design este o soluție software reutilizabilă pentru un anumit tip de problemă care apare frecvent atunci când dezvoltați software. De-a lungul anilor de practică a dezvoltării software-ului, experții au dat seama că au rezolvat probleme similare. Aceste soluții au fost încapsulate în modele de design. Asa de:

  • modelele sunt soluții dovedite pentru problemele de dezvoltare software
  • modelele sunt scalabile, deoarece acestea sunt de obicei structurate și au reguli pe care ar trebui să le urmați
  • modelele sunt reutilizabile pentru probleme similare

Vom primi câteva exemple de modele de design în continuare în tutorial.


Tipuri de modele de design

În dezvoltarea de software, modelele de design sunt în general grupate în câteva categorii. Vom acoperi cele trei cele mai importante din acest tutorial. Acestea sunt explicate pe scurt în cele ce urmează:

  1. Creational modelele se concentrează asupra modalităților de a crea obiecte sau clase. Acest lucru poate suna simplu (și este în unele cazuri), dar aplicațiile mari trebuie să controleze procesul de creare a obiectului.
  2. Structural modelele de design se concentrează asupra modalităților de a gestiona relațiile dintre obiecte astfel încât aplicația dvs. să fie arhitectată într-un mod scalabil. Un aspect cheie al modelelor structurale este acela de a vă asigura că o modificare a unei părți a aplicației dvs. nu afectează toate celelalte părți.
  3. Comportamental modelele se concentrează pe comunicarea dintre obiecte.

Este posibil să aveți întrebări după citirea acestor descrieri scurte. Acest lucru este natural și lucrurile se vor clarifica odată ce ne uităm la câteva modele de design în profunzime de mai jos. Așa că citiți mai departe!


O notă despre clasele în JavaScript

Când citiți despre modelele de design, veți vedea de multe ori referințe la clase și obiecte. Acest lucru poate fi confuz, deoarece JavaScript nu are într-adevăr construcția de "clasă"; un termen mai corect este "tipul de date".

Tipuri de date în JavaScript

JavaScript este un limbaj orientat pe obiecte în care obiectele moștenesc de la alte obiecte într-un concept cunoscut ca moștenire prototypică. Un tip de date poate fi creat definindu-se ceea ce se numește funcția constructor, cum ar fi:

funcția Persoană (config) this.name = config.name; this.age = config.age;  Person.prototype.getAge = funcția () return this.age; ; var tilo = persoană nouă (name: "Tilo", vârsta: 23); console.log (tilo.getAge ());

Rețineți utilizarea prototip atunci când se definesc metodele pe Persoană tip de date. Deoarece multiple Persoană obiectele vor face referire la același prototip, acest lucru permite getAge () metode care să fie împărtășite de toate instanțele Persoană tip de date, mai degrabă decât redefinirea pentru fiecare instanță. În plus, orice tip de date care moștenește de la Persoană vor avea acces la getAge () metodă.

Confruntarea cu confidențialitatea

O altă problemă obișnuită în JavaScript este că nu există nici un sens real al variabilelor private. Cu toate acestea, putem folosi închiderile pentru a simula oarecare confidențialitate. Luați în considerare următorul fragment:

var retinaMacbook = (functie () // variabile private var RAM, addRAM; RAM = 4; // metoda privata addRAM = functie (additionalRAM) RAM + = additionalRAM; return // variabile publice si metode USB: undefined , insertUSB: funcția (dispozitiv) this.USB = dispozitiv;, removeUSB: function () var device = this.USB; this.USB = undefined; return device;;);

În exemplul de mai sus, am creat o retinaMacbook obiect, cu variabile și metode publice și private. Acesta este modul în care îl vom folosi:

retinaMacbook.insertUSB ( "myUSB"); console.log (retinaMacbook.USB); // log out "myUSB" consola.log (retinaMacbook.RAM) // log out out undefined

Există mult mai multe lucruri pe care le putem face cu funcțiile și închiderile din JavaScript, dar nu vom ajunge în toate acestea în acest tutorial. Cu această lecție puțin despre tipurile de date JavaScript și despre confidențialitatea din spatele nostru, putem continua să învățăm despre modelele de design.


Modele de design creativ

Există multe tipuri diferite de modele de design creator, dar vom acoperi două dintre ele în acest tutorial: Builder și Prototype. Cred că acestea sunt folosite destul de des pentru a atrage atenția.

Model de constructor

Modelul Builder este adesea folosit în dezvoltarea de aplicații web și probabil că l-ați folosit mai devreme, fără ao realiza. Pur și simplu, acest model poate fi definit astfel:

Aplicând modelul constructorului ne permite să construim obiecte doar prin specificarea tipului și a conținutului obiectului. Nu trebuie să creăm în mod explicit obiectul.

De exemplu, probabil că ați făcut acest lucru nenumărate ori în jQuery:

var myDiv = $ ('
Acesta este un div.
„); // myDiv reprezintă acum un obiect jQuery care face referire la un nod DOM. var someText = $ ('

„); // someText este un obiect jQuery referindu-se la o intrare HTMLParagraphElement var input = $ ('„);

Uitați-vă la cele trei exemple de mai sus. În primul, am trecut în a

element cu un anumit conținut. În al doilea rând, am trecut într-un gol

etichetă. În ultimul, am trecut într-un element. Rezultatul celor trei a fost același: am fost returnat un obiect jQuery referindu-se la un nod DOM.

$ variabila adoptă modelul de constructor în jQuery. În fiecare exemplu, am fost returnați un obiect jQuery DOM și am avut acces la toate metodele furnizate de biblioteca jQuery, dar în nici un moment nu am sunat explicit document.createElement. Biblioteca JS a gestionat toate acestea sub capotă.

Imaginați-vă cât de mult ar fi dacă ar fi trebuit să creați explicit elementul DOM și să inserați conținut în el! Utilizând modelul constructorului, putem să ne concentrăm asupra tipului și conținutului obiectului, mai degrabă decât asupra creării sale explicite.

Model de prototip

Anterior, am trecut prin modul de definire a tipurilor de date în JavaScript prin funcții și adăugarea de metode la obiect prototip. Modelul prototip permite obiectelor să moștenească de la alte obiecte, prin prototipurile lor.

Modelul prototip este un model în care obiectele sunt create pe baza unui șablon al unui obiect existent prin clonare.

Aceasta este o modalitate ușoară și firească de a implementa moștenirea în JavaScript. De exemplu:

var Persoana = num2: 2, numHeads: 1, numHands: 2; //Object.create ia primul argument și îl aplică prototipului noului obiect. var tilo = Object.create (Persoană); console.log (tilo.numHeads); // ieșiri 1 tilo.numHeads = 2; console.log (tilo.numHeads) // ieșiri 2

Proprietățile (și metodele) din Persoană Obiectul se aplică la prototipul lui Tilo obiect. Putem redefini proprietățile de pe Tilo obiect dacă vrem să fie diferite.

În exemplul de mai sus, am folosit Object.create (). Cu toate acestea, Internet Explorer 8 nu acceptă metoda mai recentă. În aceste cazuri, putem simula comportamentul său:

var vehiculPrototype = init: function (carModel) this.model = carModel; , getModel: function () console.log ("Modelul acestui vehicul este" + this.model "); ; funcția vehicul (model) funcția F () ; F.prototype = vehiculPrototype; var f = nou F (); f.init (model); return f;  var car = vehicul ("Ford Escort"); car.getModel ();

Singurul dezavantaj al acestei metode este acela că nu puteți specifica proprietăți read-only, care pot fi specificate atunci când se utilizează Object.create (). Cu toate acestea, modelul prototip arată modul în care obiectele pot moșteni de la alte obiecte.


Modele de proiectare structurală

Modelele de proiectare structurală sunt într-adevăr utile atunci când găsim modul în care ar trebui să funcționeze un sistem. Ele permit aplicațiilor noastre să scadă ușor și să rămână menținute. Vom analiza următoarele modele din acest grup: Compozit și Fațadă.

Modelul compozit

Modelul compozit este un alt model pe care probabil l-ați folosit înainte, fără a fi realizat.

Modelul compozit spune că un grup de obiecte pot fi tratate în același mod ca un obiect individual al grupului.

Deci ce înseamnă asta? Considerăm acest exemplu în jQuery (cele mai multe biblioteci JS vor avea un echivalent cu acesta):

$ ( 'MyList ') addClass (' selectat').; . $ ( '# MyItem') addClass ( 'selectat'); // nu face acest lucru pe mese mari, este doar un exemplu. $ ("# dataTable tbody tr") pe ("click", functie (eveniment) alert ($ (this) .text ());); $ ('# myButton') la ("faceți clic pe", funcția (eveniment) alert ("Apăsat."););

Majoritatea bibliotecilor JavaScript oferă un API consistent, indiferent dacă avem de-a face cu un singur element DOM sau cu o serie de elemente DOM. În primul exemplu, putem adăuga selectat clasa la toate elementele ridicate de către .lista mea selector, dar putem folosi aceeași metodă atunci când ne ocupăm de un element DOM singular, #myItem. În mod similar, putem atașa manipulatorii de evenimente utilizând pe() pe mai multe noduri sau pe un singur nod prin același API.

Folosind modelul compozit, jQuery (și multe alte biblioteci) ne oferă un API simplificat.

Modelul compozit poate provoca uneori și probleme. Într-un limbaj slab tipizat, cum ar fi JavaScript, poate fi adesea util să știți dacă avem de-a face cu un singur element sau cu mai multe elemente. Deoarece modelul compozit utilizează același API pentru ambele, putem greși adesea unul cu celălalt și ajungem la bug-uri neașteptate. Unele librării, cum ar fi YUI3, oferă două metode separate de a obține elemente (Y.one () vs Y.all ()).

Fațadă

Iată un alt model comun pe care îl considerăm de la sine înțeles. De fapt, acesta este unul dintre preferatele mele, pentru că e simplu și am văzut că este folosit peste tot pentru a ajuta la inconsecvențele browserului. Iată ce este modelul de fațadă:

Modelul de fațadă oferă utilizatorului o interfață simplă, ascunzând complexitatea acestuia.

Modelul de fațadă îmbunătățește aproape întotdeauna utilizarea unui software. Folosind din nou jQuery ca exemplu, una dintre cele mai populare metode ale bibliotecii este gata() metodă:

$ (document) .ready (funcția () // tot codul dvs. merge aici ...);

gata() metoda pune în practică o fațadă. Dacă vă uitați la sursă, iată ce găsiți:

gata: (funcția () ... // Mozilla, Opera și Webkit dacă (document.addEventListener) document.addEventListener ("DOMContentLoaded", idempotent_fn, false) // asigurarea arderii inainte de incarcarea; poate fi tarziu dar si in siguranta pentru iframes document.attachEvent ("onreadystatechange", idempotent_fn); ...)

Sub capotă, gata() metoda nu este atât de simplă. jQuery normalizează neconcordanțele browserului pentru a se asigura că acesta gata() este concediat la momentul potrivit. Cu toate acestea, în calitate de dezvoltator, vi se oferă o interfață simplă.

Cele mai multe exemple ale modelului de fațadă respectă acest principiu. Atunci când implementăm una, de obicei, ne bazăm pe declarații condiționate sub capotă, dar prezentăm-o ca o interfață simplă pentru utilizator. Alte metode care implementează acest model includ anima() și css (). Vă puteți gândi de ce ar folosi un model de fațadă?


Modele de design comportamentale

Orice sistem informatic orientat pe obiecte va avea comunicare între obiecte. Nu organizarea acestei comunicări poate duce la bug-uri care sunt greu de găsit și de reparații. Modelele de design comportamentale prevăd diferite metode de organizare a comunicării între obiecte. În această secțiune, vom examina modelele Observer și Mediator.

Modelul de observator

Modelul Observer este primul dintre cele două modele comportamentale pe care le vom trece. Iată ce spune:

În modelul Observer, un subiect poate avea o listă de observatori care sunt interesați de ciclul de viață al acestuia. Oricând subiectul face ceva interesant, trimite o notificare observatorilor săi. Dacă un observator nu mai este interesat să asculte subiectul, subiectul îl poate elimina din listă.

Suna destul de simplu, nu? Avem nevoie de trei metode pentru a descrie acest model:

  • publica (date): Chemată de subiect când are o notificare de făcut. Unele date pot fi transmise prin această metodă.
  • aboneze (observator): Numit de subiect pentru a adăuga un observator la lista sa de observatori.
  • dezabonare (observator): Numit de subiect pentru a elimina un observator din lista sa de observatori.

Se pare că majoritatea bibliotecilor JavaScript moderne acceptă aceste trei metode ca parte a infrastructurii evenimentelor personalizate. De obicei, există un pe() sau atașați () metoda, a declanșare () sau foc() și o metodă off () sau desprinde() metodă. Luați în considerare următorul fragment:

// Am creat doar o asociere între metodele jQuery
// și cele prescrise de modelul de observator, dar nu trebuie. var o = $ (); $ .subscribe = o.on.bind (o); $ .unsubscribe = o.off.bind (o); $ .publish = o.trigger.bind (o); // Utilizarea document.on ('tweetsReceived', funcția (tweets) // efectuarea unor acțiuni, apoi declanșarea unui eveniment $ .publish ('tweetsShow', tweets);); // Ne putem abona la acest eveniment și apoi ne putem declanșa evenimentul. $ .subscribe ('tweetsShow', functie () // afiseaza tweets cumva ... // publica o actiune dupa ce sunt afisate $ .publish ('tweetsDisplayed);); $ .subscribe ('tweetsDisplayed, funcția () ...);

Modelul Observer este unul dintre cele mai simple modele de implementare, dar este foarte puternic. JavaScript este potrivit pentru a adopta acest tipar deoarece este în mod natural bazat pe evenimente. Data viitoare când dezvoltați aplicații web, gândiți-vă la dezvoltarea unor module care sunt cuplate în mod loos între ele și adoptă modelul Observer ca mijloc de comunicare. Modelul de observator poate deveni problematică dacă sunt implicați prea mulți subiecți și observatori. Acest lucru se poate întâmpla în sistemele de mari dimensiuni, iar următorul model pe care îl analizăm încearcă să rezolve această problemă.

Modelul mediatorului

Ultimul model la care ne vom uita este modelul de mediator. Este similar cu modelul Observer, dar cu unele diferențe notabile.

Modelul de mediator promovează utilizarea unui singur subiect comun care gestionează comunicarea cu mai multe obiecte. Toate obiectele comunică între ele prin intermediul mediatorului.

O analogie reală în lumea reală ar fi un Turn de Trafic Aerian, care gestionează comunicarea dintre aeroport și zboruri. În lumea dezvoltării software-ului, modelul Mediator este adesea folosit ca un sistem foarte complicat. Prin plasarea mediatorilor, comunicarea poate fi tratată printr-un singur obiect, mai degrabă decât având obiecte multiple care comunică între ele. În acest sens, un model de mediator poate fi folosit pentru a înlocui un sistem care implementează modelul de observator.

Există o implementare simplificată a modelului Mediator de către Addy Osmani în acest conținut. Să vorbim despre cum să-l folosești. Imaginați-vă că aveți o aplicație web care permite utilizatorilor să facă clic pe un album și să redea muzică din acesta. Ai putea să înființezi un astfel de mediator:

$ ('# album') pe ('click', funcția (e) e.preventDefault (); var albumId = $ (this) .id;) mediator.publish ("playAlbum" ; var playAlbum = funcție (id) ... mediator.publish ("albumStartedPlaying", songList: [...], currentSong: "Fără tine"); ; var logAlbumPlayed = funcția (id) // Înregistrați albumul din backend; var updateUserInterface = funcție (album) // Update UI pentru a reflecta ceea ce se redă; // Abonamente mediator mediator.subscribe ("playAlbum", playAlbum); mediator.subscribe ("playAlbum", logAlbumPlayed); mediator.subscribe ("albumStartedPlaying", updateUserInterface);

Beneficiul acestui model asupra modelului Observer este acela că un singur obiect este responsabil de comunicare, în timp ce în modelul de observator obiectele multiple ar putea fi ascultate și abonate unul la altul.

În modelul Observer, nu există un obiect unic care să încapsuleze o constrângere. În schimb, observatorul și subiectul trebuie să coopereze pentru a menține constrângerea. Tipurile de comunicare sunt determinate de modul în care observatorii și subiecții sunt interconectați: un singur subiect de obicei are mulți observatori și, uneori, observatorul unui subiect este subiect al unui alt observator.


Concluzie

Cineva a aplicat-o deja cu succes în trecut.

Lucrul minunat al modelelor de design este că cineva a aplicat-o deja cu succes în trecut. Există o mulțime de cod open-source care implementează diverse modele în JavaScript. În calitate de dezvoltatori, trebuie să fim conștienți de ce tipare există și când să le aplicăm. Sper că acest tutorial te-a ajutat să faci încă un pas în a răspunde la aceste întrebări.


Citire suplimentară

O mare parte din conținutul acestui articol poate fi găsit în cartea Excel Learning Design Patterns, de Addy Osmani. Este o carte online care a fost lansată gratuit sub licența Creative Commons. Cartea cuprinde extensiv teoria și implementarea multor modele, atât în ​​JavaScript, cât și în diferite biblioteci JS. Vă încurajez să o considerați o referință atunci când începeți următorul proiect.

Cod