Могу ли я улучшить этот метод с помощью утки?
Надеюсь, я не неправильно понял значение "утиной типизации", но из того, что я прочитал, это означает, что я должен писать код, основываясь на том, как объект реагирует на методы, а не на том, какой это тип / класс.
Вот код:
def convert_hash(hash)
if hash.keys.all? { |k| k.is_a?(Integer) }
return hash
elsif hash.keys.all? { |k| k.is_a?(Property) }
new_hash = {}
hash.each_pair {|k,v| new_hash[k.id] = v}
return new_hash
else
raise "Custom attribute keys should be ID's or Property objects"
end
end
Что я хочу, так это убедиться, что я получаю хеш, где ключи представляют собой целое число, представляющее идентификатор объекта ActiveRecord. Мне не особо нравится дважды перебирать хеш-ключи all?
чтобы определить, нужно ли мне забрать удостоверение личности.
Конечно, я приму любые другие предложения по улучшению этого кода:)
3 ответа
То, как вы пишете этот метод, должно зависеть от того, ожидаете ли вы исключения в ходе нормального выполнения программы. Если вам нужно читаемое сообщение об исключении, потому что его может увидеть конечный пользователь, то его выбрасывание вручную имеет смысл. В противном случае я бы просто сделал что-то вроде этого:
def convert(hash)
new_hash = {}
hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v }
return new_hash
end
Это выполнит в точности то же самое, и вы все равно получите исключение, если ключ массива не имеет поля id. Более того, здесь используется немного больше утки, потому что теперь все, что имеет поле id, будет приемлемым, что лучше, чем явная проверка на наличие свойства. Это делает ваш код более гибким, особенно при модульном тестировании.
У нас все еще есть явная проверка для целочисленных объектов, но этот случайный особый случай обычно приемлем, особенно при проверке встроенных типов данных.
Утиная печать на самом деле просто нюансированная версия полиморфизма. В статически типизированном языке, таком как Java, вам нужно было бы создать явный интерфейс, который бы сообщал компилятору обо всех методах, которые может принимать конкретная переменная. В динамическом языке, таком как Ruby, интерфейсы все еще существуют в абстрактном смысле, они просто неявные.
Проблема в том, что вы принимаете две разные структуры данных в одном методе. Чтобы заставить утку набирать текст, нужно требовать, чтобы все объекты, которые передаются вашему методу, подчинялись одному и тому же контракту (т. Е. Это всегда хеш целых чисел для объектов [Foo]). Процесс преобразования хеша с ключами свойства в Правильная структура должна быть заданием клиентского кода. Это можно сделать очень просто с помощью простого класса-обертки или функции преобразования, состоящей только из тела вашего предложения elseif.
Суть в том, что парень, вызывающий метод, должен убедиться, что все его параметры работают так, как ваш метод ожидает их. Если они этого не сделают, ему нужно выяснить, как сделать свою индейку шарлатаном, как утку, а не вы.
Что я хочу, так это убедиться, что я получаю хеш, где ключи представляют собой целое число, представляющее идентификатор объекта ActiveRecord.
Вероятно, вы должны проверить это при создании / вставке в хеш. Вы можете попробовать что-то вроде этого:
h = {} def h.put obj самостоятельно [obj.id]= OBJ конец
или, может быть
h = {} def h.[]= ключ, значение поднять "ад", если ключ == value.id супер конец