door Nishant Mishra
als student Informatica besteed ik veel tijd aan het leren en spelen met nieuwe talen. Elke nieuwe taal heeft iets unieks te bieden. Dat gezegd hebbende, beginnen de meeste beginners hun programmeertraject met ofwel procedurele talen zoals C of met objectgeoriënteerde talen zoals JavaScript en C++.
daarom is het zinvol om de basisprincipes van objectgeoriënteerd programmeren door te nemen, zodat u de concepten kunt begrijpen en ze gemakkelijk kunt toepassen op de talen die u leert. We zullen de Ruby programmeertaal als voorbeeld gebruiken.
u vraagt zich misschien af waarom Ruby? Omdat het” ontworpen is om programmeurs gelukkig te maken ” en ook omdat bijna alles in Ruby een object is.
een idee krijgen van het Object-Oriented paradigma (OOP)
in OOP identificeren we de “dingen” die ons programma behandelt. Als mensen denken we over dingen als objecten met eigenschappen en gedrag, en we interageren met dingen op basis van deze eigenschappen en gedrag. Een ding kan een auto zijn, een boek, enzovoort. Zulke dingen worden klassen (de blauwdrukken van objecten), en we maken objecten uit deze klassen.
elke instantie (object) bevat instantievariabelen die de status van het object (attributen) zijn. Object gedrag wordt vertegenwoordigd door methoden.
laten we het voorbeeld van een auto nemen. Een auto is iets wat het een klasse zou maken. Een specifiek type auto, zeggen BMW is een object van de klasse auto. De attributen / eigenschappen van een BMW zoals de kleur en het modelnummer kunnen worden opgeslagen in instance variabelen. En als u een bewerking van het object wilt uitvoeren, zoals rijden, beschrijft “rijden” een gedrag dat wordt gedefinieerd als een methode.
een snelle syntaxis Les
- om een regel in een Ruby programma te beëindigen, een puntkomma(;) is optioneel, maar wordt meestal niet gebruikt)
- 2-ruimte inspringing voor elke geneste niveau wordt bevorderd (niet vereist is, zoals het is in Python)
- Geen accolades
{}
worden gebruikt, en het einde het woord wordt gebruikt om het einde van het flow-control block - commentaar Te geven, we maken gebruik van de
De manier waarop objecten worden gemaakt in Ruby is door te bellen naar een nieuwe methode van een klasse, zoals in het voorbeeld hieronder:
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
om Te begrijpen wat er gaande is in de code hierboven:
- We hebben een klasse met de naam
Car
met twee methodes,initialize
enget_info
. - instantievariabelen in Ruby beginnen met
@
(bijvoorbeeld@name
). Het interessante is dat de variabelen niet in eerste instantie worden gedeclareerd. Ze ontstaan wanneer ze voor het eerst worden gebruikt, en daarna zijn ze beschikbaar voor alle instance-methoden van de klasse. - het aanroepen van de
new
methode zorgt ervoor dat deinitialize
methode wordt aangeroepen.initialize
is een speciale methode die wordt gebruikt als constructor.
toegang tot gegevens
instantievariabelen zijn privé en kunnen niet worden benaderd van buiten de klasse. Om toegang te krijgen, moeten we methoden creëren. Instance methoden hebben standaard publieke toegang. We kunnen de toegang tot deze instantie methoden beperken zoals we later in dit artikel zullen zien.
om de gegevens te verkrijgen en te wijzigen, hebben we respectievelijk “getter” en “setter” methoden nodig. Laten we eens kijken naar deze methoden met hetzelfde voorbeeld van een auto.
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
in Ruby worden de” getter “en de” setter ” gedefinieerd met dezelfde naam als de instantie variabele waar we mee te maken hebben.
in het voorbeeld hierboven, als we my_car.color
zeggen, Roept het eigenlijk de color
methode aan die op zijn beurt de naam van de kleur retourneert.
Opmerking: let op hoe Ruby een spatie tussen de color
en is gelijk toestaat om te ondertekenen tijdens het gebruik van de setter, ook al is de methodenaam color=
door deze getter/setter methoden te schrijven, hebben we meer controle. Maar meestal, het verkrijgen van de bestaande waarde en het instellen van een nieuwe waarde is eenvoudig. Zo, Er moet een gemakkelijkere manier in plaats van eigenlijk het definiëren van getter / setter methoden.
de gemakkelijkere manier
door in plaats daarvan het attr_*
formulier te gebruiken, kunnen we de bestaande waarde verkrijgen en een nieuwe waarde instellen.
-
attr_accessor
: voor getter en setter beide -
attr_reader
: alleen voor getter -
attr_writer
: voor setter only
laten we eens kijken naar dit formulier met hetzelfde voorbeeld van een auto.
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
op deze manier kunnen we de getter/setter definities helemaal overslaan.
sprekend over best practices
in het voorbeeld hierboven hebben we de waarden voor de variabelen @name
en @color
niet geïnitialiseerd, wat geen goede praktijk is. Aangezien de instantievariabelen op nul staan, heeft het object car1
ook geen zin. Het is altijd een goede gewoonte om instance variabelen in te stellen met behulp van een constructor zoals in het voorbeeld hieronder.
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
Klasse methoden en klasse variabelen
dus klasse methoden worden aangeroepen op een klasse, niet op een instantie van een klasse. Deze zijn vergelijkbaar met statische methoden in Java.
Noot: self
buiten de definitie van de methode verwijst naar het klasse-object. Klasse variabelen beginnen met @@
nu zijn er drie manieren om Klasse methoden in Ruby te definiëren:
binnen de klasse definitie
- met behulp van het trefwoord self met de naam van de methode:
class MathFunctions def self.two_times(num) num * 2 endend
# No instance createdputs MathFunctions.two_times(10) # => 20
2. Gebruik <<
; zelf
class MathFunctions class << self def two_times(num) num * 2 end endend
# No instance createdputs MathFunctions.two_times(10) # => 20
buiten de definitie van klasse
3. Class name gebruiken met de method name
class MathFunctionsend
def MathFunctions.two_times(num) num * 2end
# No instance createdputs MathFunctions.two_times(10) # => 20
Klasse-overerving
in Ruby erft elke klasse impliciet van de objectklasse. Laten we naar een voorbeeld kijken.
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}" # => Car1 Top speed 100puts "#{fast_car}2 #{fast_car.speed}" # => Car2 Top speed 200
in het bovenstaande voorbeeld heeft de klasse SuperCar
voorrang op de methode speed
die is overgenomen van de klasse Car
. Het symbool &
lt; geeft overerving aan.
opmerking: Ruby ondersteunt geen meervoudige overerving, en dus worden in plaats daarvan mix-ins gebruikt. We zullen ze later in dit artikel bespreken.
Modules in Ruby
een Ruby-module is een belangrijk onderdeel van de Ruby-programmeertaal. Het is een belangrijke object-georiënteerde eigenschap van de taal en ondersteunt meerdere overerving indirect.
een module is een container voor klassen, methoden, constanten of zelfs andere modules. Net als een klasse, kan een module niet worden geà nstalleerd, maar dient twee hoofddoelen:
- naamruimte
- Mix-in
Modules als naamruimte
veel talen zoals Java hebben het idee van de pakketstructuur, om botsingen tussen twee klassen te voorkomen. Laten we eens kijken naar een voorbeeld om te begrijpen hoe het werkt.
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
in het voorbeeld hierboven, omdat we twee klassen hebben met de naam Match
, kunnen we er een onderscheid tussen maken en botsingen voorkomen door ze simpelweg in verschillende modules te plaatsen.
Modules als Mix-in
In het objectgeoriënteerde paradigma hebben we het concept van Interfaces. Mix-in biedt een manier om code te delen tussen meerdere klassen. Niet alleen dat, we kunnen ook de ingebouwde modules zoals Enumerable
opnemen en onze taak veel gemakkelijker maken. Laat een voorbeeld zien.
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 # => Name: Nishant
organization = Organization.neworganization.name = "freeCodeCamp"puts organization.print_it # => Name: freeCodeCamp
Mix-ins zijn extreem krachtig, omdat we de code slechts één keer schrijven en ze vervolgens overal kunnen opnemen zoals vereist.
Scope in Ruby
zullen we zien hoe scope werkt voor:
- variabelen
- constanten
- blokken
bereik van variabelen
methoden en klassen definiëren een nieuw bereik voor variabelen, en variabelen buiten het bereik worden niet overgedragen naar de binnen het bereik. Eens kijken wat dit betekent.
name = "Nishant"
class MyClass def my_fun name = "John" puts name # => John end
puts name # => Nishant
de buitenste name
variabele en de binnenste name
variabele zijn niet hetzelfde. De outer name
variabele wordt niet overgedragen naar de inner scope. Dat betekent dat als je het in de binnenste scope probeert af te drukken zonder het opnieuw te definiëren, er een uitzondering wordt gemaakt — zo ‘ n variabele bestaat niet
Scope van constanten
een binnen scope kan constanten zien gedefinieerd in de buitenste scope en kan ook de buitenste constanten overschrijven. Maar het is belangrijk om te onthouden dat zelfs na het overschrijven van de constante waarde in de innerlijke scope, de waarde in de buitenste scope onveranderd blijft. Laten we het in actie zien.
module MyModule PI = 3.14 class MyClass def value_of_pi puts PI # => 3.14 PI = "3.144444" puts PI # => 3.144444 end end puts PI # => 3.14end
Scope van blokken
blokken erven de outer scope. Laten we het begrijpen met behulp van een fantastisch voorbeeld dat ik vond op het 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 =
total_sum = 0accts.each do |eachAcct| total_sum = total_sum + eachAcct.amountend
puts total_sum # => 900
in het bovenstaande voorbeeld, als we een methode gebruiken om de total_sum
te berekenen, zou de total_sum
variabele een totaal andere variabele binnen de methode zijn. Daarom kan het gebruik van blokken ons soms veel tijd besparen.
dit gezegd zijnde, is een variabele die in het blok wordt aangemaakt alleen beschikbaar voor het blok.
Toegangscontrole
bij het ontwerpen van een klasse is het belangrijk na te denken over hoeveel ervan u aan de wereld zult blootstellen. Dit staat bekend als inkapseling, en betekent meestal het verbergen van de interne representatie van het object.
er zijn drie niveaus van toegangscontrole in Ruby:
- publiek – er wordt geen Toegangscontrole afgedwongen. Iedereen kan deze methoden noemen.
- beschermd-kan worden aangeroepen door objecten van de definiërende klassen of de subklassen ervan.
- privé-kan niet worden aangeroepen, behalve met een expliciete ontvanger.
laten we een voorbeeld van inkapseling in Actie bekijken:
class Car def initialize(speed, fuel_eco) @rating = speed * comfort end
def rating @rating endend
puts Car.new(100, 5).rating # => 500
nu, als de details van hoe de rating wordt berekend binnen de klasse worden gehouden, kunnen we het op elk moment in de tijd veranderen zonder enige andere verandering. Ook kunnen we de rating niet van buitenaf instellen.
over de manieren om toegangscontrole te specificeren, zijn er twee:
- het opgeven van publiek, beschermd, of privé en alles tot het volgende sleutelwoord voor toegangscontrole zal dat toegangscontroleniveau hebben.
- Definieer de methode regelmatig, specificeer vervolgens publieke, private en beschermde toegangsniveaus en geef de door komma ‘ s gescheiden methoden op onder die niveaus met behulp van methodesymbolen.
voorbeeld van de eerste manier:
class MyClass private def func1 "private" end protected def func2 "protected" end public def func3 "Public" endend
voorbeeld van de tweede weg:
class MyClass def func1 "private" end def func2 "protected" end def func3 "Public" end private :func1 protected :func2 public :func3end
opmerking: de publieke en private toegangscontroles worden het meest gebruikt.
conclusie
dit zijn de basisprincipes van objectgeoriënteerd programmeren in Ruby. Als je deze concepten kent, kun je dieper gaan en ze leren door coole dingen te bouwen.
vergeet niet te klappen en te volgen als je genoten hebt! Hou me bij.