by Nishant Mishra
jako student informatyki spędzam dużo czasu na nauce i zabawie z nowymi językami. Każdy nowy język ma coś wyjątkowego do zaoferowania. Mimo to, większość początkujących zaczyna swoją przygodę z programowaniem od języków proceduralnych, takich jak C, lub od języków obiektowych, takich jak JavaScript i C++.
dlatego warto przejść przez podstawy programowania obiektowego, aby zrozumieć pojęcia i łatwo zastosować je do języków, których się uczysz. Jako przykład użyjemy języka programowania Ruby.
możesz zapytać, dlaczego Ruby? Ponieważ jest „zaprojektowany, aby uszczęśliwiać programistów”, a także dlatego, że prawie wszystko w Rubim jest obiektem.
poznanie paradygmatu Obiektowego (OOP)
w OOP identyfikujemy „rzeczy”, które nasz program obsługuje. Jako ludzie myślimy o rzeczach jako o obiektach posiadających atrybuty i zachowania, i oddziałujemy z nimi w oparciu o te atrybuty i zachowania. Rzecz może być samochodem, książką i tak dalej. Takie rzeczy stają się klasami (schematami obiektów), a my tworzymy obiekty z tych klas.
każda instancja (obiekt) zawiera zmienne instancji, które są stanem obiektu (atrybutami). Zachowania obiektowe są reprezentowane przez metody.
weźmy przykład samochodu. Samochód to rzecz, która uczyniłaby z niego klasę. Specyficzny typ samochodu, powiedzmy BMW jest obiektem klasy samochodu. Atrybuty / właściwości BMW, takie jak kolor i numer modelu, mogą być przechowywane w zmiennych instancji. A jeśli chcesz wykonać operację obiektu, taką jak jazda, to „drive” opisuje zachowanie, które jest zdefiniowane jako metoda.
szybka Lekcja składni
- kończąca linię w programie Ruby, średnik(;
- zalecane jest wcięcie 2-spacji dla każdego zagnieżdżonego poziomu (nie jest wymagane, tak jak w Pythonie)
- nie są używane klamry
{}
, a słowo kluczowe end jest używane do oznaczania końca bloku kontroli przepływu - aby skomentować, używamy symbolu
#
sposób tworzenia obiektów w Ruby polega na wywołaniu nowej metody w klasie, jak w poniższym przykładzie:
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
aby zrozumieć, co dzieje się w powyższym kodzie:
- mamy klasę o nazwie
Car
z dwiema metodami,initialize
iget_info
. - zmienne instancji w Rubim zaczynają się od
@
(na przykład@name
). Interesujące jest to, że zmienne nie są początkowo deklarowane. Powstają one po pierwszym użyciu, a następnie są dostępne dla wszystkich metod instancji klasy. - wywołanie metody
new
powoduje wywołanie metodyinitialize
.initialize
jest specjalną metodą, która jest używana jako konstruktor.
dostęp do danych
zmienne instancji są prywatne i nie mogą być dostępne spoza klasy. Aby uzyskać do nich dostęp, musimy stworzyć metody. Metody instancji mają domyślnie publiczny dostęp. Możemy ograniczyć dostęp do tych metod instancji, jak zobaczymy w dalszej części tego artykułu.
aby uzyskać i zmodyfikować dane, potrzebujemy odpowiednio metod „getter” i „setter”. Spójrzmy na te metody, biorąc ten sam przykład samochodu.
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
w Rubim, „getter ” i” setter ” są zdefiniowane z tą samą nazwą co zmienna instancji, z którą mamy do czynienia.
w powyższym przykładzie, kiedy mówimy my_car.color
, w rzeczywistości wywołuje metodę color
, która z kolei zwraca nazwę koloru.
Notatka: zwróć uwagę na to, jak Ruby pozwala na znak pomiędzy color
i równymi podczas używania setera, nawet jeśli nazwa metody to color=
zapis tych metod getter/setter pozwala nam mieć większą kontrolę. Ale przez większość czasu uzyskanie istniejącej wartości i ustawienie nowej wartości jest proste. Tak więc powinien być łatwiejszy sposób, zamiast faktycznie definiować metody getter/setter.
łatwiejszy sposób
używając formularza attr_*
, możemy uzyskać istniejącą wartość i ustawić nową wartość.
-
attr_accessor
: zarówno dla gettera, jak i setera -
attr_reader
: tylko dla gettera -
attr_writer
: dla setera tylko
spójrzmy na ten formularz biorąc ten sam przykład samochodu.
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
w ten sposób możemy całkowicie pominąć definicje getter/setter.
mówiąc o najlepszych praktykach
w powyższym przykładzie nie zainicjowaliśmy wartości zmiennych instancji @name
i @color
, co nie jest dobrą praktyką. Ponadto, ponieważ zmienne instancji są ustawione na nil, obiekt car1
nie ma sensu. Zawsze dobrą praktyką jest ustawianie zmiennych instancji przy użyciu konstruktora, jak w poniższym przykładzie.
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
metody klas I zmienne klas
tak więc metody klas są wywoływane na klasie, a nie na instancji klasy. Są one podobne do metod statycznych w Javie.
Uwaga: self
poza definicją metody odnosi się do obiektu klasy. Zmienne klasy zaczynają się od @@
teraz istnieją trzy sposoby definiowania metod klasy w Rubim:
wewnątrz definicji klasy
- używając słowa kluczowego self z nazwą metody:
class MathFunctions def self.two_times(num) num * 2 endend
# No instance createdputs MathFunctions.two_times(10) # => 20
2. Using <<
; self
class MathFunctions class << self def two_times(num) num * 2 end endend
# No instance createdputs MathFunctions.two_times(10) # => 20
poza definicją klasy
3. Używanie nazwy klasy z nazwą metody
class MathFunctionsend
def MathFunctions.two_times(num) num * 2end
# No instance createdputs MathFunctions.two_times(10) # => 20
dziedziczenie klas
w Rubim każda klasa domyślnie dziedziczy z klasy obiektu. Spójrzmy na przykład.
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
w powyższym przykładzie Klasa SuperCar
nadpisuje metodę speed
, która jest dziedziczona z klasy Car
. Symbol &
lt; oznacza dziedziczenie.
Uwaga: Ruby nie obsługuje wielokrotnego dziedziczenia, więc zamiast tego używa się mieszanek. Omówimy je później w tym artykule.
Moduły w języku Ruby
moduł Ruby jest ważną częścią języka programowania Ruby. Jest to główna obiektowa cecha języka i pośrednio wspiera wielokrotne dziedziczenie.
moduł jest kontenerem dla klas, metod, stałych, a nawet innych modułów. Podobnie jak Klasa, moduł nie może być instancjowany, ale służy dwóm głównym celom:
- Przestrzeń nazw
- Mix-in
Moduły jako przestrzeń nazw
wiele języków takich jak Java ma ideę struktury pakietów, aby uniknąć kolizji między dwiema klasami. Spójrzmy na przykład, aby zrozumieć, jak to działa.
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
w powyższym przykładzie, ponieważ mamy dwie klasy o nazwie Match
, możemy rozróżnić je i zapobiec kolizji, po prostu zamykając je w różne moduły.
Moduły jako Mix-in
w paradygmacie obiektowym mamy pojęcie interfejsów. Funkcja Mix-in umożliwia współdzielenie kodu między wieloma klasami. Nie tylko to, możemy również włączyć wbudowane moduły, takie jak Enumerable
i znacznie ułatwić nasze zadanie. Zobaczmy przykład.
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
miksowanie jest niezwykle potężne, ponieważ piszemy Kod tylko raz, a następnie możemy go umieścić w dowolnym miejscu.
Scope w Ruby
zobaczymy jak działa scope dla:
- zmienne
- stałe
- bloki
zakres zmiennych
metody i klasy definiują nowy zakres zmiennych, a zewnętrzne zmienne zakresu nie są przenoszone do wewnętrznego zakresu. Zobaczmy, co to znaczy.
name = "Nishant"
class MyClass def my_fun name = "John" puts name # => John end
puts name # => Nishant
zewnętrzna zmienna name
i wewnętrzna name
nie są takie same. Zewnętrzna zmienna name
nie zostaje przeniesiona do wewnętrznego zakresu. Oznacza to, że jeśli spróbujesz wydrukować go w wewnętrznym zakresie bez ponownego definiowania go, zostanie wyrzucony wyjątek — żadna taka zmienna nie istnieje
zakres stałych
wewnętrzny zakres może widzieć stałe zdefiniowane w zewnętrznym zakresie i może również nadpisać zewnętrzne stałe. Ale ważne jest, aby pamiętać, że nawet po nadpisaniu stałej w zakresie wewnętrznym, wartość w zakresie zewnętrznym pozostaje niezmieniona. Zobaczmy to w akcji.
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
zakres bloków
bloki dziedziczą zewnętrzny zakres. Zrozumiemy to na podstawie fantastycznego przykładu, który znalazłem w Internecie.
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
w powyższym przykładzie, jeśli użyjemy metody do obliczenia total_sum
, zmienna total_sum
będzie zupełnie inną zmienną wewnątrz metody. Dlatego czasami używanie klocków może zaoszczędzić nam dużo czasu.
powiedziawszy to, zmienna utworzona wewnątrz bloku jest dostępna tylko dla bloku.
Kontrola dostępu
podczas projektowania klasy, ważne jest, aby pomyśleć o tym, jak wiele z nich będziesz wystawiać na świat. Jest to znane jako enkapsulacja i zazwyczaj oznacza ukrycie wewnętrznej reprezentacji obiektu.
istnieją trzy poziomy kontroli dostępu w Ruby:
- publiczne – nie jest egzekwowana Kontrola dostępu. Każdy może nazwać te metody.
- Protected – może być wywoływany przez obiekty klas definiujących lub ich podklasy.
- Private – nie może być wywoływany z wyjątkiem jawnego odbiornika.
zobaczmy przykład enkapsulacji w akcji:
class Car def initialize(speed, fuel_eco) @rating = speed * comfort end
def rating @rating endend
puts Car.new(100, 5).rating # => 500
teraz, ponieważ szczegóły dotyczące sposobu obliczania oceny są przechowywane w klasie, możemy ją zmienić w dowolnym momencie bez żadnych innych zmian. Nie możemy również ustawić ratingu z zewnątrz.
mówiąc o sposobach określenia kontroli dostępu, są dwa z nich:
- określanie publicznego, chronionego lub prywatnego i wszystkiego, aż do następnego słowa kluczowego Kontrola dostępu będzie miało ten poziom kontroli dostępu.
- regularnie Definiuj metodę, a następnie określaj poziomy dostępu publicznego, prywatnego i chronionego i wyświetlaj listy metod rozdzielonych przecinkami na tych poziomach za pomocą symboli metod.
przykład pierwszej drogi:
class MyClass private def func1 "private" end protected def func2 "protected" end public def func3 "Public" endend
przykład drugiej drogi:
class MyClass def func1 "private" end def func2 "protected" end def func3 "Public" end private :func1 protected :func2 public :func3end
Uwaga: najczęściej używane są publiczne i prywatne kontrole dostępu.
podsumowanie
oto podstawy programowania obiektowego w Rubim. Teraz, znając te koncepcje, możesz pójść głębiej i nauczyć się ich, budując fajne rzeczy.
nie zapomnij klaskać i śledzić, jeśli ci się podobało! Nadążaj za mną.