de Nishant Mishra

O introducere în programarea orientată pe obiecte cu Ruby

O introducere in programarea orientata pe obiecte cu Ruby

În calitate de student la informatică, petrec mult timp învățând și jucându-mă cu limbi noi. Fiecare limbă nouă are ceva unic de oferit. Acestea fiind spuse, majoritatea începătorilor își încep călătoria de programare fie cu limbaje procedurale, cum ar fi C, fie cu limbaje orientate obiect, cum ar fi JavaScript și C ++.

Prin urmare, este logic să parcurgeți elementele de bază ale programării orientate pe obiecte, astfel încât să puteți înțelege conceptele și să le aplicați limbajelor pe care le învățați cu ușurință. Vom folosi limbajul de programare Ruby ca exemplu.

Poate te întrebi, de ce Ruby? Pentru că este „conceput pentru a face fericiți programatorii” și, de asemenea, pentru că aproape totul în Ruby este un obiect.

Obținerea unui sentiment al paradigmei orientate pe obiecte (OOP)

În POO, identificăm „lucrurile” pe care le gestionează programul nostru. Ca oameni, ne gândim la lucruri ca la obiecte cu atribute și comportamente și interacționăm cu lucruri pe baza acestor atribute și comportamente. Un lucru poate fi o mașină, o carte și așa mai departe. Astfel de lucruri devin clase (planurile obiectelor), iar noi creăm obiecte din aceste clase.

Fiecare instanță (obiect) conține variabile de instanță care sunt starea obiectului (atribute). Comportamentele obiectelor sunt reprezentate prin metode.

Să luăm exemplul unei mașini. O mașină este un lucru care ar face din ea o clasă. Un tip specific de mașină, spunem că BMW este un obiect din clasa Mașină. atribute / proprietăți unui BMW, cum ar fi culoarea și numărul modelului, pot fi stocate în variabile de instanță. Și dacă doriți să efectuați o operație a obiectului, cum ar fi conducerea, atunci „conduce” descrie un comportament care este definit ca un metodă.

O lecție rapidă de sintaxă

  • Pentru a termina o linie într-un program Ruby, un punct și virgulă (;) este opțional (dar în general nu este utilizat)
  • Indentarea cu 2 spații pentru fiecare nivel imbricat este încurajată (nu este necesară, așa cum este în Python)
  • Fără acolade {} sunt utilizate, iar Sfârșit cuvântul cheie este folosit pentru a marca sfârșitul unui bloc de control al fluxului
  • Pentru a comenta, folosim # simbol

Modul în care obiectele sunt create în Ruby este prin apelarea unui nou metoda pe o clasă, ca în exemplul de mai jos:

class Car  def initialize(name, color)    @name = name    @color = color  end
  def get_info    "Name: #{@name}, and Color: #{@color}"  endend
my_car = Car.new("Fiat", "Red")puts my_car.get_info

Pentru a înțelege ce se întâmplă în codul de mai sus:

  • Avem o clasă numită Car cu două metode, initialize și get_info.
  • Variabilele de instanță din Ruby încep cu @ (De exemplu @name). Partea interesantă este că variabilele nu sunt declarate inițial. Ele apar în existență atunci când sunt utilizate pentru prima dată și apoi sunt disponibile pentru toate metodele de instanță ale clasei.
  • Apelarea la new metoda cauzează initialize metodă de invocare. initialize este o metodă specială care este utilizată ca constructor.

Accesarea datelor

Variabilele de instanță sunt private și nu pot fi accesate din afara clasei. Pentru a le accesa, trebuie să creăm metode. Metodele de instanță au acces public în mod implicit. Putem limita accesul la aceste metode de instanță, așa cum vom vedea mai târziu în acest articol.

Pentru a obține și modifica datele, avem nevoie de metode „getter” și respectiv „setter”. Să ne uităm la aceste metode luând același exemplu de mașină.

class Car  def initialize(name, color) # "Constructor"    @name = name    @color = color  end
  def color    @color  end
  def color= (new_color)    @color = new_color  endend
my_car = Car.new("Fiat", "Red")puts my_car.color # Red
my_car.color = "White"puts my_car.color # White

În Ruby, „getter” și „setter” sunt definite cu același nume ca variabila de instanță cu care avem de-a face.

În exemplul de mai sus, când spunem my_car.color, apelează de fapt color metoda care la rândul său returnează numele culorii.

Notă: Acordați atenție modului în care Ruby permite un spațiu între color și este egal cu semnarea în timp ce utilizați setterul, chiar dacă numele metodei este color=

Scrierea acestor metode getter / setter ne permite să avem mai mult control. Dar, de cele mai multe ori, obținerea valorii existente și setarea unei noi valori este simplă. Deci, ar trebui să existe o modalitate mai ușoară în loc să definiți de fapt metodele getter / setter.

Modul mai ușor

Prin utilizarea attr_* în schimb, putem obține valoarea existentă și putem seta o nouă valoare.

  • attr_accessor: pentru getter și setter atât
  • attr_reader: doar pentru getter
  • attr_writer: doar pentru setter

Să ne uităm la această formă luând același exemplu de mașină.

class Car  attr_accessor :name, :colorend
car1 = Car.newputs car1.name # => nil
car1.name = "Suzuki"car1.color = "Gray"puts car1.color # => Gray
car1.name = "Fiat"puts car1.name # => Fiat

În acest fel putem sări peste definițiile getter / setter.

Vorbind despre cele mai bune practici

În exemplul de mai sus, nu am inițializat valorile pentru @name și @color variabile de instanță, ceea ce nu este o bună practică. De asemenea, deoarece variabilele de instanță sunt setate la zero, obiectul car1 nu are niciun sens. Este întotdeauna o practică bună să setați variabile de instanță folosind un constructor, ca în exemplul de mai jos.

class Car  attr_accessor :name, :color    def initialize(name, color)    @name = name    @color = color  endend
car1 = Car.new("Suzuki", "Gray")puts car1.color # => Gray
car1.name = "Fiat"puts car1.name # => Fiat

Metode de clasă și variabile de clasă

Deci metodele de clasă sunt invocate pe o clasă, nu pe o instanță a unei clase. Acestea sunt similare cu static metode în Java.

Notă: self în afara definiției metodei se referă la obiectul clasei. Variabilele de clasă încep cu @@

Acum, există de fapt trei moduri de a defini metodele de clasă în Ruby:

În interiorul definiției clasei

  1. Folosind cuvântul cheie self cu numele metodei:
class MathFunctions  def self.two_times(num)    num * 2  endend
# No instance createdputs MathFunctions.two_times(10) # => 20

2. Folosind <<; de sine

class MathFunctions  class << self    def two_times(num)      num * 2    end  endend
# No instance createdputs MathFunctions.two_times(10) # =&gt; 20

În afara definiției clasei

3. Utilizarea numelui clasei cu numele metodei

class MathFunctionsend
def MathFunctions.two_times(num)  num * 2end
# No instance createdputs MathFunctions.two_times(10) # =&gt; 20

Moștenirea clasei

În Ruby, fiecare clasă moștenește implicit din clasa Object. Să vedem un exemplu.

class Car  def to_s    "Car"  end
  def speed    "Top speed 100"  endend
class SuperCar < Car  def speed # Override    "Top speed 200"  endend
car = Car.newfast_car = SuperCar.new
puts "#{car}1 #{car.speed}" # =&gt; Car1 Top speed 100puts "#{fast_car}2 #{fast_car.speed}" # => Car2 Top speed 200

În exemplul de mai sus, SuperCar clasa suprascrie speed metoda care este moștenită de la Car clasă. Simbolul &lt; denotă moștenire.

Notă: Ruby nu acceptă moștenirea multiplă, astfel încât sunt utilizate în schimb mix-in-uri. Le vom discuta mai târziu în acest articol.

Module în Ruby

Un modul Ruby este o parte importantă a limbajului de programare Ruby. Este o caracteristică majoră a limbajului orientată spre obiecte și acceptă moștenirea multiplă indirect.

Un modul este un container pentru clase, metode, constante sau chiar alte module. La fel ca o clasă, un modul nu poate fi instanțiat, dar are două scopuri principale:

  • Spațiu de nume
  • Mix-in

Module ca spațiu de nume

O mulțime de limbaje precum Java au ideea structurii pachetelor, doar pentru a evita coliziunea între două clase. Să analizăm un exemplu pentru a înțelege cum funcționează.

module Patterns  class Match    attr_accessor :matched  endend
module Sports  class Match    attr_accessor :score  endend
match1 = Patterns::Match.newmatch1.matched = "true"
match2 = Sports::Match.newmatch2.score = 210

În exemplul de mai sus, așa cum avem două clase numite Match, putem diferenția între ele și preveni coliziunea prin simpla încapsulare a acestora în diferite module.

Module ca Mix-in

În paradigma orientată obiect, avem conceptul de interfețe. Mix-in oferă o modalitate de a partaja codul între mai multe clase. Nu numai asta, putem include și module încorporate precum Enumerable și ușurează-ne sarcina. Să vedem un exemplu.

module PrintName  attr_accessor :name  def print_it    puts "Name: #{@name}"  endend
class Person  include PrintNameend
class Organization  include PrintNameend
person = Person.newperson.name = "Nishant"puts person.print_it # =&gt; Name: Nishant
organization = Organization.neworganization.name = "Routech"puts organization.print_it # =&gt; Name: Routech 

Mix-urile sunt extrem de puternice, deoarece scriem codul o singură dată și apoi le putem include oriunde, după cum este necesar.

Domeniul de aplicare în Ruby

Vom vedea cum funcționează domeniul de aplicare pentru:

  • variabile
  • constante
  • blocuri

Domeniul de aplicare al variabilelor

Metodele și clasele definesc un nou domeniu de aplicare pentru variabile, iar variabilele de domeniu extern nu sunt reportate în domeniul de aplicare interior. Să vedem ce înseamnă asta.

name = "Nishant"
class MyClass  def my_fun    name = "John"    puts name # =&gt; John  end
puts name # =&gt; Nishant

Exterior name variabil și interior name variabile nu sunt aceleași. Exterior name variabila nu este transferată la domeniul interior. Asta înseamnă că, dacă încercați să-l imprimați în sfera interioară fără a o defini din nou, ar fi aruncată o excepție – nu există o astfel de variabilă

Domeniul de aplicare al constantelor

Un domeniu interior poate vedea constante definite în domeniul exterior și poate, de asemenea, să le suprascrie constantele exterioare. Dar este important să ne amintim că, chiar și după înlocuirea valorii constante din domeniul interior, valoarea din domeniul exterior rămâne neschimbată. Să o vedem în acțiune.

module MyModule  PI = 3.14  class MyClass    def value_of_pi      puts PI # =&gt; 3.14      PI = "3.144444"      puts PI # => 3.144444    end  end  puts PI # =&gt; 3.14end

Domeniul de aplicare al blocurilor

Blocurile moștenesc domeniul de aplicare exterior. Să o înțelegem folosind un exemplu fantastic pe care l-am găsit pe internet.

class BankAccount  attr_accessor :id, :amount  def initialize(id, amount)    @id = id    @amount = amount  endend
acct1 = BankAccount.new(213, 300)acct2 = BankAccount.new(22, 100)acct3 = BankAccount.new(222, 500)
accts = [acct1, acct2, acct3]
total_sum = 0accts.each do |eachAcct|  total_sum = total_sum + eachAcct.amountend
puts total_sum # =&gt; 900

În exemplul de mai sus, dacă folosim o metodă pentru a calcula total_sum, total_sum variabila ar fi o variabilă total diferită în interiorul metodei. De aceea, uneori, folosirea blocurilor ne poate economisi mult timp.

Acestea fiind spuse, o variabilă creată în interiorul blocului este disponibilă numai pentru bloc.

Controlul accesului

Când proiectați o clasă, este important să vă gândiți cât de mult din ea veți expune lumii. Acest lucru este cunoscut sub numele de Incapsulare, și de obicei înseamnă ascunderea reprezentării interne a obiectului.

Există trei niveluri de control al accesului în Ruby:

  • Public – nu se impune controlul accesului. Oricine poate apela la aceste metode.
  • Protejat – poate fi invocat de obiecte ale claselor definitorii sau ale subclaselor sale.
  • Privat – nu poate fi invocat decât cu un receptor explicit.

Să vedem un exemplu de încapsulare în acțiune:

class Car  def initialize(speed, fuel_eco)    @rating = speed * comfort  end
  def rating    @rating  endend
puts Car.new(100, 5).rating # =&gt; 500

Acum, întrucât detaliile despre modul în care este calculată evaluarea sunt păstrate în cadrul clasei, o putem schimba în orice moment, fără nicio altă modificare. De asemenea, nu putem seta ratingul din exterior.

Vorbind despre modalitățile de specificare a controlului accesului, există două dintre acestea:

  1. Specificarea publică, protejată sau privată și totul până la următorul cuvânt cheie control acces va avea acel nivel de control acces.
  2. Definiți metoda în mod regulat, apoi specificați nivelurile de acces publice, private și protejate și enumerați metodele separate prin virgulă (,) sub acele niveluri folosind simbolurile metodei.

Exemplu de prima cale:

class MyClass  private    def func1      "private"    end  protected    def func2      "protected"    end  public    def func3      "Public"    endend

Exemplu de a doua cale:

class MyClass  def func1    "private"  end  def func2    "protected"  end  def func3    "Public"  end  private :func1  protected :func2  public :func3end

Notă: se utilizează cel mai mult controalele de acces public și privat.

Concluzie

Acestea sunt elementele de bază ale programării orientate pe obiecte în Ruby. Acum, cunoscând aceste concepte, puteți merge mai adânc și le puteți învăța construind lucruri interesante.

Nu uitați să bateți din palme și să urmăriți dacă v-a plăcut! Ține pasul cu mine Aici.