Menu
Coddy logo textTech

self в методах

Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 9 из 57.

Внутри метода name = value всегда создаёт локальную переменную, даже если у класса есть сеттер name=. Чтобы вызвать сеттер, укажите получатель: self.name = value:

class Player
  attr_accessor :level

  def initialize
    @level = 1
  end

  def broken_level_up
    level = level + 1
  rescue NoMethodError
    puts "level is a new local variable here, still nil"
  end

  def level_up
    self.level = level + 1
  end
end

p1 = Player.new
p1.broken_level_up
p1.level_up
puts p1.level

Вывод:

level is a new local variable here, still nil
2

Читателям не нужно self.: level и self.level вызывают один и тот же метод. В обычном стиле self. пишут только там, где это необходимо Ruby, — перед сеттером:

class Rect
  attr_accessor :w, :h

  def initialize(w, h)
    @w = w
    @h = h
  end

  def area
    w * h
  end

  def double!
    self.w = w * 2
    self.h = self.h * 2
    self
  end
end

puts Rect.new(2, 3).double!.area

Результат:

24

Вызов сеттера из initialize с помощью self. позволяет новым объектам проходить ту же проверку, что и последующим изменениям, поэтому правило находится в одном месте:

class Product
  attr_reader :price

  def initialize(price)
    self.price = price
  end

  def price=(value)
    raise ArgumentError, "price must be positive" if value <= 0

    @price = value
  end
end

puts Product.new(5).price
begin
  Product.new(0)
rescue ArgumentError => e
  puts e.message
end

Результат:

5
price must be positive

То, чем является self, зависит от того, где выполняется код. Внутри метода экземпляра это экземпляр; в теле класса, вне любого метода, это сам класс:

class Robot
  puts "in the class body, self is #{self}"

  def whoami
    "in a method, self is a #{self.class}"
  end
end

puts Robot.new.whoami

Вывод:

in the class body, self is Robot
in a method, self is a Robot
challenge icon

Задание

Легко

У класса Account есть пользовательский сеттер balance=, который вызывает ошибку ArgumentError с сообщением balance cannot be negative для значения меньше 0. Заверши initialize(owner, balance), deposit(amount) и withdraw(amount) так, чтобы каждое изменение баланса проходило через этот сеттер с использованием self., включая начальный баланс.

Предоставленный код считывает имя владельца и начальный баланс, затем обрабатывает команды +n (пополнение) или -n (снятие). После каждой команды он выводит Ada: 120 либо сообщение об ошибке и неизменённый баланс. При недопустимом начальном балансе выводится только сообщение об ошибке.

Твой код должен находиться в account.rb. В main.rb содержится предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

require_relative 'account'

# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
  input << line.chomp
end
begin
  account = Account.new(input[0], input[1].to_i)
  input.drop(2).each do |cmd|
    begin
      amount = cmd[1..].to_i
      cmd.start_with?("+") ? account.deposit(amount) : account.withdraw(amount)
    rescue ArgumentError => e
      puts e.message
    end
    puts "#{account.owner}: #{account.balance}"
  end
rescue ArgumentError => e
  puts e.message
end
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Ruby