Могу ли я улучшить этот метод с помощью утки? - PullRequest
5 голосов
/ 16 октября 2008

Надеюсь, я не неправильно понял значение "типизирования утки", но из того, что я прочитал, это означает, что я должен писать код на основе того, как объект реагирует на методы, а не на то, какой это тип / класс.

Вот код:

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 ]

11 голосов
/ 16 октября 2008

То, как вы пишете этот метод, должно зависеть от того, ожидаете ли вы исключения в ходе нормального выполнения программы. Если вам нужно читаемое сообщение об исключении, потому что его может увидеть конечный пользователь, то выбрасывать его вручную имеет смысл. В противном случае, я бы просто сделал что-то вроде этого:

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, будет приемлемым, что лучше, чем явная проверка на наличие свойства. Это делает ваш код более гибким, особенно при модульном тестировании.

У нас все еще есть явная проверка для целочисленных объектов, но этот случайный особый случай обычно приемлем, особенно при проверке встроенных типов данных.

3 голосов
/ 16 октября 2008

Утиная печать - это всего лишь тонкая версия полиморфизма. В статически типизированном языке, таком как Java, вам нужно будет создать явный интерфейс, который сообщит компилятору все методы, которые может принимать конкретная переменная. В динамическом языке, таком как Ruby, интерфейсы все еще существуют в абстрактном смысле, они просто неявные.

Проблема в том, что вы принимаете две разные структуры данных в одном методе. Чтобы заставить утку набирать текст, нужно требовать, чтобы все объекты, которые передаются вашему методу, подчинялись одному и тому же контракту (т. Е. Это всегда хэш Integer для объектов [Foo].) Правильная структура должна быть заданием клиентского кода. Это можно сделать очень просто с помощью простого класса-обертки или функции преобразования, состоящей только из тела вашего предложения elseif.

Суть в том, что парень, вызывающий метод, должен убедиться, что все его параметры работают так, как ваш метод ожидает их. Если они этого не сделают, ему нужно выяснить, как сделать свою индюшатину крякать, как утку, а не вы.

0 голосов
/ 17 октября 2008

Что мне нужно, так это убедиться, что я получаю хеш, где ключи являются целым числом, представляющим идентификатор объекта ActiveRecord.

Возможно, вам следует проверить это при создании / вставке в хеш. Вы можете попробовать что-то вроде этого:

h = {}
def h.put obj
  self[obj.id]=obj
end

или, может быть

h = {}
def h.[]= key, value
  raise "hell" unless key == value.id
  super
end
...