В Ruby переменные с разными префиксами (@, @@, $) ведут себя по-разному, и понимание их областей видимости критично для написания чистого кода. В этой статье разберёмся, чем отличаются переменные экземпляра, класса и глобальные, как они влияют на архитектуру приложения и почему @@ лучше избегать. Особое внимание уделим подводным камням, которые могут возникнуть при работе с наследованием и глобальными состояниями.
Если ты когда-то на собеседовании перепутал @, @@ и $, знай: ты не один.
Сегодня мы расставим всё по полочкам (и смахнём пыль с @@).
🥸 Переменные в Ruby — в лицах
| Вид переменной | Синтаксис | Где живёт | Видимость |
|---|---|---|---|
| Локальная | x |
Внутри метода/блока | только там |
| Экземпляра | @x |
Внутри объекта | у каждого экземпляра |
| Класса | @@x |
Внутри класса и его наследников | у всего дерева |
| Глобальная | $x |
Во всей программе | везде (🤢) |
| Константа | X |
В модуле или классе | через :: |
🧪 Примеры в бою
class Spaceship
@@fleet_size = 0 # переменная класса
def initialize(name)
@name = name # переменная экземпляра
@@fleet_size += 1
end
def self.fleet_size
@@fleet_size
end
def name
@name
end
end
s1 = Spaceship.new("Apollo")
s2 = Spaceship.new("Falcon")
puts s1.name # => Apollo
puts Spaceship.fleet_size # => 2
🧬 Чем отличается @ от @@?
class Parent
@@var = "from parent"
@var = "instance var of Parent"
def self.vars
[@@var, @var]
end
end
class Child < Parent
@@var = "from child"
@var = "instance var of Child"
end
puts Parent.vars.inspect # => ["from child", "instance var of Parent"]
puts Child.vars.inspect # => ["from child", "instance var of Child"]
Вывод:
@@var— одна на всё дерево: любые изменения — везде.@var— разные переменные: одна уParent, другая уChild.
💀 Зачем избегать @@?
- Делится с наследниками — нарушает инкапсуляцию.
- Трудно отследить, где изменилась.
- Баги в проде → нытьё в Jira.
Совет: почти всегда лучше использовать @ на уровне class << self:
class MyClass
class << self
attr_accessor :my_var
end
end
🗺 А что с $переменными?
$x = 42
class Demo
def show
puts $x
end
end
Demo.new.show # => 42
- Глобально.
- Бессмысленно.
- Не надо так.
📌 Мнемоника:
@— у объекта (@личное)@@— у всех потомков (двойной геморрой)$— всем пофиг, но видно везде
❗ Популярный подвох:
class A
def self.set
@@x = 1
end
end
class B < A
end
B.set
puts A.class_variable_get(:@@x) # => 1
@@x завёлся в родителе, но установили его из наследника.
🔚 Вывод:
Переменные с символами в Ruby — это не просто синтаксис, это целый стиль мышления.
Если хочешь меньше боли — забудь про @@ и тем более про $. Будь современным рубистом — и пиши @ в нужном месте.