У Ruby змінні, що починаються з @, @@ та $, мають різні рівні видимості та використання (скоуп). Розглянемо приклади та спробуємо максимально просто пояснити відмінності.
@ або @instance_variable
Змінні, що починаються з @, є змінними екземпляра. Вони належать конкретному об'єкту (екземпляру класу) і є доступними тільки в межах цього об'єкта. Кожен екземпляр класу має свої власні змінні екземпляра. Перевіримо:
class Example
def initialize(value)
@instance_variable = value
end
def show
@instance_variable
end
end
obj1 = Example.new(10)
obj2 = Example.new(20)
puts obj1.show # Виведе 10
puts obj2.show # Виведе 20
Метод ініціалізації об'єкта приймає значення і встановлює його у змінну екземпляра (коли ми викликаємо метод
show на об'єкті
obj1 спочатку виконується ініціалізація а потім сам метод). Метод show повертає значення змінної екземпляра.
Тобто змінна яка задається за допомогою символу
@ дає можливість шейрити значення серед методів класу в контексті об'єкту. Кожен новий об'єкт, наприклад obj2 буде мати своє значення у змінній @instance_variable.
@@ або @@class_variable
Змінні, що починаються з @@, є змінними класу. Вони спільні для всіх екземплярів класу. Якщо одна з змінних класу змінюється в одному екземплярі класу, ця зміна буде видима в усіх інших екземплярах цього класу.
Простий приклад:
class Example
@@class_variable = 0
def self.increment
@@class_variable += 1
end
def self.show
@@class_variable
end
end
Example.increment
Example.increment
puts Example.show # Виведе 2
Тобто змінні класу впливають на всі об'єкти цього класу. Іноді це доволі небезпечно і треба бути дуже уважними, щоб не наробити шкоди вплинувши на всі об'єкти.
$ або $global_variable
Змінні, що починаються з $, є глобальними змінними. Вони доступні з будь-якої частини програми, незалежно від області видимості. Використання глобальних змінних може ускладнювати відстеження стану програми, тому їх варто уникати, якщо це можливо.
Приклад:
$global_variable = 10
def show_global
$global_variable
end
puts show_global # Виведе 10
Приклад не дуже наочний, але технічно - ця глобальна змінна може бути змінена та використана будь-де в програмі.
Коротко підсумовуючи
- @instance_variable: змінна екземпляра, унікальна для кожного об'єкта.
- @@class_variable: змінна класу, спільна для всіх екземплярів класу.
- $global_variable: глобальна змінна, доступна з будь-якої частини програми.
Ну й вчимось писати тести - завжди можна перевірити якісь твердження за допомогою тестів.
test.rb
require 'rspec'
class Example
@@class_variable = 0
$global_variable = 0
def initialize(value)
@instance_variable = value
end
def increment_class_variable
@@class_variable += 1
end
def increment_global_variable
$global_variable += 1
end
def instance_variable
@instance_variable
end
def self.class_variable
@@class_variable
end
def self.global_variable
$global_variable
end
end
RSpec.describe 'Різні типи змінних' do
before do
@example1 = Example.new(1)
@example2 = Example.new(2)
end
context 'змінні екземпляра' do
it 'унікальні для кожного об\'єкта' do
expect(@example1.instance_variable).to eq(1)
expect(@example2.instance_variable).to eq(2)
end
end
context 'змінні класу' do
it 'спільні для всіх екземплярів класу' do
@example1.increment_class_variable
@example2.increment_class_variable
expect(Example.class_variable).to eq(2)
end
end
context 'глобальні змінні' do
it 'доступні з будь-якої частини програми' do
@example1.increment_global_variable
@example2.increment_global_variable
expect(Example.global_variable).to eq(2)
end
end
end
rspec test.rb ...Finished in 0.01932 seconds (files took 0.28601 seconds to load)
3 examples, 0 failures