Ruby: Можно ли, чтобы метод возвращал объекты разных типов?

Стоит ли возвращать объект, тип которого меняется в зависимости от внутренней логики метода?

Например.:

class Error
  attr_reader :details

  def initialize(details)
    @details = details
  end
end

def div(a, b)
  return Error.new("error: division by zero") if b == 0
  a / b
end

# declare foo, bar here
result = div(foo, bar)
if result.is_a?(Error)
  puts result.details
else
  puts "result of division: #{result}"
end

Как видите, метод div возвращает либо экземпляр Error, либо экземпляр Integer.

Является ли это плохой практикой (и почему)? Нарушает ли это принцип единой ответственности?

Кстати, я понимаю, что другим вариантом может быть возврат хеша { error: error, div_result: div_result }, но мне любопытно, можно ли его заменить только одним единственным объектом.

Спасибо.

🤔 А знаете ли вы, что...
Ruby имеет богатую стандартную библиотеку, которая упрощает разработку приложений.


3
62
1

Ответ:

Решено

Вы, кажется, заново изобретаете велосипед. В Ruby уже есть обработка исключений , а также ZeroDivisionError, которая возникает при делении целого числа на ноль:

def div(a, b)
  a / b
end

begin
  result = div(6, 0)
  puts "result of division: #{result}"
rescue ZeroDivisionError => e
  puts "error: #{e.message}"
end

# prints "error: divided by 0"

Обратите внимание, что ваш метод div также не нужен, вы также можете написать result = 6 / 0.


Стоит ли возвращать объект, тип которого меняется в зависимости от внутренней логики метода?

«тип» — сложный термин в Ruby. Я предполагаю, что вы имеете в виду что-то вроде «экземпляр определенного класса», например, целое число или строку? Это действительно зависит. Для большинства методов это, безусловно, было бы хорошей идеей, но из этого правила есть хорошие исключения.

Например, Integer#+ возвращает результат на основе своего аргумента: (можно утверждать, что все это подклассы Numeric)

1 + 2    #=> 3       (Integer)
1 + 2.0  #=> 3.0     (Float)
1 + 2r   #=> (3/1)   (Rational)

String#index обычно возвращает целое число, но также может возвращать nil, чтобы указать «не найдено»:

"hello".index("o") #=> 4     (Integer)
"hello".index("x") #=> nil   (NilClass)

Array#[] возвращает любой объект, хранящийся по указанному индексу, поэтому возвращаемые значения произвольны:

a = [123, :foo, "bar"]

a[0] #=> 123    (Integer)
a[1] #=> :foo   (Symbol)
a[2] #=> "bar"  (String)
a[3] #=> nil    (NilClass)