de Rainer Hahnekamp

O introducere în programarea orientată pe obiecte în JavaScript

O introducere in programarea orientata pe obiecte in JavaScript
JavaScript și programare orientată pe obiecte

Acest articol este destinat studenților cu JavaScript care nu au cunoștințe anterioare în programarea orientată obiect (OOP). Mă concentrez pe părțile din POO care sunt relevante numai pentru JavaScript și nu pe POO în general. Omit polimorfismul pentru că se potrivește mai bine cu un limbaj de tip static.

De ce trebuie să știi asta?

Ați ales JavaScript pentru a fi primul dvs. limbaj de programare? Vrei să fii un dezvoltator hot-shot care lucrează pe sisteme de întreprindere uriașe care acoperă sute de mii de linii de cod sau mai mult?

Dacă nu înveți să îmbrățișezi pe deplin programarea orientată pe obiecte, vei fi bine și cu adevărat pierdut.

Mentalități diferite

În fotbal, poți juca dintr-o apărare sigură, poți juca cu mingi înalte din lateral sau poți ataca de parcă nu ar fi mâine. Toate aceste strategii au același obiectiv: Să câștigi jocul.

Același lucru este valabil și pentru paradigmele de programare. Există diferite moduri de a aborda o problemă și de a proiecta o soluție.

Programarea orientată pe obiecte sau OOP este paradigma dezvoltării moderne a aplicațiilor. Este acceptat de limbaje majore precum Java, C # sau JavaScript.

Paradigma orientată spre obiecte

Din perspectiva OOP, o aplicație este o colecție de „obiecte” care comunică între ele. Bazăm aceste obiecte pe lucruri din lumea reală, cum ar fi produsele din inventar sau înregistrările angajaților. Obiectele conțin date și realizează o anumită logică pe baza datelor lor. Prin urmare, codul OOP este foarte ușor de înțeles. Ceea ce nu este atât de ușor este să decideți cum să împărțiți o aplicație în aceste obiecte mici, în primul rând.

Dacă ai fi fost ca mine când l-am auzit prima dată, nu ai nici o idee despre ce înseamnă acest lucru – totul sună foarte abstract. Să te simți așa este absolut bine. Este mai important să fi auzit ideea, să ți-o amintești și să încerci să aplici OOP în cod. În timp, veți câștiga experiență și veți alinia mai mult codul dvs. la acest concept teoretic.

Lecţie: OOP bazat pe obiecte din lumea reală permite oricui să vă citească codul și să înțeleagă ce se întâmplă.

Obiect ca piesă centrală

1611250329 360 O introducere in programarea orientata pe obiecte in JavaScript

Un exemplu simplu vă va ajuta să vedeți cum JavaScript implementează principiile fundamentale ale OOP. Luați în considerare un caz de utilizare a cumpărăturilor în care introduceți produsele în coș și apoi calculați prețul total pe care trebuie să îl plătiți. Dacă luați cunoștințele dvs. JavaScript și codificați cazul de utilizare fără OOP, ar arăta astfel:

const bread = {name: 'Bread', price: 1};const water = {name: 'Water', price: 0.25};
const basket = [];basket.push(bread);basket.push(bread);basket.push(water);basket.push(water);basket.push(water);
const total = basket  .map(product => product.price)  .reduce((a, b) => a + b, 0);
console.log('one has to pay in total: ' + total);

Perspectiva OOP facilitează scrierea unui cod mai bun, deoarece ne gândim la obiecte așa cum le-am întâlni în lumea reală. Deoarece cazul nostru de utilizare conține un coș de produse, avem deja două tipuri de obiecte – obiectul coș și obiectele produsului.

Versiunea OOP a cazului de utilizare pentru cumpărături ar putea fi scrisă astfel:

const bread = new Product('bread', 1);const water = new Product('water', .25)const basket = new Basket();basket.addProduct(2, bread);basket.addProduct(3, water);basket.printShoppingInfo();

După cum puteți vedea în prima linie, creăm un obiect nou folosind cuvântul cheie new urmat de numele a ceea ce se numește clasă (descrisă mai jos). Aceasta returnează un obiect pe care îl stocăm variabilului pâine. Repetăm ​​asta pentru apa variabilă și luăm o cale similară pentru a crea un coș variabil. După ce ați adăugat aceste produse la coș, în final imprimați suma totală pe care trebuie să o plătiți.

Diferența dintre cele două fragmente de cod este evidentă. Versiunea OOP aproape citește ca niște propoziții reale în limba engleză și puteți spune cu ușurință ce se întâmplă.

Lecţie: Un obiect modelat pe lucruri din lumea reală constă din date și funcții.

Clasa ca șablon

1611250329 944 O introducere in programarea orientata pe obiecte in JavaScript

Folosim clase în OOP ca șabloane pentru crearea obiectelor. Un obiect este o „instanță a unei clase”, iar „instanțierea” este crearea unui obiect bazat pe o clasă. Codul este definit în clasă, dar nu poate fi executat decât dacă se află într-un obiect live.

Puteți privi cursuri precum planurile unei mașini. Acestea definesc proprietățile mașinii, cum ar fi cuplul și puterea, funcțiile interne, cum ar fi raportul aer-combustibil și metodele accesibile publicului, cum ar fi aprinderea. Cu toate acestea, numai atunci când o fabrică instanțiază mașina, puteți roti cheia și puteți conduce.

În cazul nostru de utilizare, folosim clasa Produs pentru a crea două obiecte, pâine și apă. Desigur, acele obiecte au nevoie de cod pe care trebuie să îl furnizați în clase. Merge astfel:

function Product(_name, _price) {  const name = _name;  const price = _price;
this.getName = function() {    return name;  };
this.getPrice = function() {    return price;  };}
function Basket() {  const products = [];
this.addProduct = function(amount, product) {    products.push(...Array(amount).fill(product));  };
this.calcTotal = function() {    return products      .map(product => product.getPrice())      .reduce((a, b) => a + b, 0);  };
this.printShoppingInfo = function() {    console.log('one has to pay in total: ' + this.calcTotal());  };}

O clasă în JavaScript arată ca o funcție, dar o folosiți diferit. Numele funcției este numele clasei și este scris cu majuscule. Deoarece nu returnează nimic, nu numim funcția în modul obișnuit ca. const basket = Product('bread', 1);. În schimb, adăugăm cuvântul cheie new like const basket = new Product('bread', 1);.

Codul din interiorul funcției este constructorul. Acest cod se execută de fiecare dată când un obiect este instanțiat. Produsul are parametrii _name și _price. Fiecare obiect nou stochează aceste valori în interiorul său.

Mai mult, putem defini funcțiile pe care obiectul le va oferi. Aceste funcții le definim prin prefixarea acestui cuvânt cheie, care le face accesibile din exterior (vezi Encapsulare). Observați că funcțiile au acces complet la proprietăți.

Class Basket nu necesită argumente pentru a crea un obiect nou. Instanțierea unui nou obiect Coș generează pur și simplu o listă goală de produse pe care programul le poate completa ulterior.

Lecţie: O clasă este un șablon pentru generarea de obiecte în timpul rulării.

Incapsularea

1611250330 615 O introducere in programarea orientata pe obiecte in JavaScript

Este posibil să întâlniți o altă versiune a modului de declarare a unei clase:

function Product(name, price) {  this.name = name;  this.price = price;}

Atenție la atribuirea proprietăților variabilei this. La prima vedere, pare a fi o versiune mai bună, deoarece nu mai necesită metodele getter (getName & getPrice) și, prin urmare, este mai scurtă.

Din păcate, ați oferit acum acces complet la proprietăți din exterior. Deci toată lumea ar putea să o acceseze și să o modifice:

const bread = new Product('bread', 1);bread.price = -10;

Acest lucru nu vă doriți, deoarece face aplicația mai dificilă de întreținut. Ce s-ar întâmpla dacă ați adăuga un cod de validare pentru a preveni, de exemplu, prețuri mai mici de zero? Orice cod care accesează direct proprietatea de preț ar ocoli validarea. Acest lucru ar putea introduce erori care ar fi dificil de urmărit. Pe de altă parte, codul care folosește metodele getter ale obiectului este garantat să treacă prin validarea prețului obiectului.

Obiectele ar trebui să aibă control exclusiv asupra datelor lor. Cu alte cuvinte, obiectele își „încapsulează” datele și împiedică alte obiecte să acceseze direct datele. Singura modalitate de a accesa datele este indirectă prin intermediul funcțiilor scrise în obiecte.

Datele și prelucrarea (aka logică) aparțin împreună. Acest lucru este valabil mai ales atunci când vine vorba de aplicații mai mari, unde este foarte important ca prelucrarea datelor să fie limitată la locuri definite în mod specific.

Gata, OOP produce modularitate prin design, sfântul Graal în dezvoltarea de software. Păstrează temutul cod spaghetti în care totul este strâns cuplat și nu știi ce se întâmplă când schimbi o bucată mică de cod.

În cazul nostru, obiectele din clasa Produs nu vă permit să modificați prețul sau numele după inițializarea lor. Instanțele produsului sunt numai în citire.

Lecţie: Incapsularea împiedică accesul la date, cu excepția funcțiilor obiectului.

Moştenire

1611250330 278 O introducere in programarea orientata pe obiecte in JavaScript

Moștenirea vă permite să creați o nouă clasă prin extinderea unei clase existente cu proprietăți și funcții suplimentare. Noua clasă „moștenește” toate caracteristicile părintelui său, evitând crearea de cod nou de la zero. În plus, orice modificare adusă clasei părinte va fi disponibilă automat clasei copil. Acest lucru face actualizările mult mai ușoare.

Să presupunem că avem o nouă clasă numită Carte care are un nume, un preț și un autor. Cu moștenirea, puteți spune că o carte este la fel ca un produs, dar cu proprietatea suplimentară a autorului. Spunem că Produsul este superclasa Cărții și Cartea este o subclasă a Produsului:

function Book(_name, _price, _author) {  Product.call(this, _name, _price);  const author = _author;    this.getAuthor = function() {    return author;  }}

Rețineți suplimentul Product.call de-a lungul this ca primul argument. Vă rugăm să rețineți: Deși cartea oferă metodele getter, totuși nu are acces direct la numele și prețul proprietăților. Cartea trebuie să apeleze acele date din clasa Produs.

Acum puteți adăuga un obiect de carte în coș fără probleme:

const faust = new Book('faust', 12.5, 'Goethe');basket.addProduct(1, faust);

Coșul așteaptă un obiect de tip Produs. Deoarece cartea moștenește de la produs prin carte, este și un produs.

Lecţie: Subclasele pot moșteni proprietăți și funcții de la superclase, adăugând în același timp proprietăți și funcții proprii.

JavaScript și OOP

Veți găsi trei paradigme de programare diferite utilizate pentru a crea aplicații JavaScript. Acestea sunt programarea bazată pe prototip, programarea orientată pe obiecte și programarea orientată funcțional.

Motivul pentru aceasta rezidă în istoria JavaScript. Inițial, a fost bazat pe prototip. JavaScript nu a fost conceput ca limbaj pentru aplicații mari.

Împotriva planului fondatorilor săi, dezvoltatorii au folosit din ce în ce mai mult JavaScript pentru aplicații mai mari. OOP a fost altoit pe tehnica originală bazată pe prototip.

Abordarea bazată pe prototip este prezentată mai jos. Este văzut ca „modalitatea clasică și implicită” de a construi clase. Din păcate, nu acceptă încapsularea.

Chiar dacă suportul JavaScript pentru OOP nu este la același nivel cu alte limbaje, cum ar fi Java, este în continuare în evoluție. Lansarea versiunii ES6 a adăugat o versiune dedicată class cuvânt cheie pe care l-am putea folosi. Pe plan intern, servește același scop ca proprietatea prototip, dar reduce dimensiunea codului. Cu toate acestea, clasele ES6 încă nu au proprietăți private, motiv pentru care m-am lipit de „vechea cale”.

Din motive de completitudine, așa vom scrie produsul, coșul și cartea cu clasele ES6 și, de asemenea, cu abordarea prototipului (clasic și implicit). Rețineți că aceste versiuni nu oferă încapsulare:

// ES6 version
class Product {  constructor(name, price) {    this.name = name;    this.price = price;  }}
class Book extends Product {  constructor(name, price, author) {    super(name, price);    this.author = author;  }}
class Basket {  constructor() {    this.products = [];  }
  addProduct(amount, product) {    this.products.push(…Array(amount).fill(product));  }
  calcTotal() {    return this.products      .map(product => product.price)      .reduce((a, b) => a + b, 0);  }
  printShoppingInfo() {    console.log('one has to pay in total: ' + this.calcTotal());  }}
const bread = new Product('bread', 1);const water = new Product('water', 0.25);const faust = new Book('faust', 12.5, 'Goethe');
const basket = new Basket();basket.addProduct(2, bread);basket.addProduct(3, water);basket.addProduct(1, faust);basket.printShoppingInfo();
//Prototype versionfunction Product(name, price) {  this.name = name;  this.price = price;}function Book(name, price, author) {  Product.call(this, name, price);  this.author = author;}Book.prototype = Object.create(Product.prototype);Book.prototype.constructor = Book;function Basket() {  this.products = [];}Basket.prototype.addProduct = function(amount, product) {  this.products.push(...Array(amount).fill(product));};Basket.prototype.calcTotal = function() {  return this.products    .map(product => product.price)    .reduce((a, b) => a + b, 0);};Basket.prototype.printShoppingInfo = function() {  console.log('one has to pay in total: ' + this.calcTotal());};

Lecţie: OOP a fost adăugat la JavaScript mai târziu în dezvoltarea sa.

rezumat

Ca nou programator care învață JavaScript, va dura mult timp pentru a aprecia pe deplin programarea orientată pe obiecte. Lucrurile importante de înțeles în acest stadiu incipient sunt principiile pe care se bazează paradigma POO și beneficiile pe care le oferă:

  • Obiectele modelate pe lucruri din lumea reală sunt centrul oricărei aplicații bazate pe OOP.
  • Incapsularea protejează datele de acces necontrolat.
  • Obiectele au funcții care operează pe datele pe care le conțin obiectele.
  • Clasele sunt șabloanele folosite pentru a crea instanțe de obiecte.
  • Moștenirea este un instrument puternic pentru evitarea redundanței.
  • OOP este mai detaliat, dar mai ușor de citit decât alte paradigme de codare.
  • Deoarece OOP a venit mai târziu în dezvoltarea JavaScript, este posibil să întâlniți coduri mai vechi care utilizează prototip sau tehnici de programare funcționale.

Lecturi suplimentare