Рефакторинг с динамически типизированным языком

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

Ruby, PHP и JavaScript являются довольно популярными языками в эти дни, и у них есть много людей, которые защищают их и утверждают, что быть с динамическим контролем типов не сдерживает разработчика. Я плохо знаком с этими языками и хотел бы начать использовать их для больших проектов, но здесь являюсь основным сценарием рефакторинга, который подходит все время на работе (работа == C#), и я задаюсь вопросом, чем подход был бы в Ruby - я выбрал Ruby, потому что это - OO.

Хорошо, я использую Ruby, и я создаю Клиентский объект. Это имеет методы для загрузки/сохранения/удаления от базы данных. Это хорошо, и люди используют его. Я добавляю больше методов для других вещей, и люди используют его больше. Я добавляю метод для вычисления истории заказов на основе некоторых параметров. К настоящему времени этот класс используется на всем протяжении системы. Затем однажды я решаю изменить параметры на методе GetOrderHistory. Так я:

  • добавьте новые параметры к методу
  • перепишите код метода для использования новых параметров
  • измените клиентский код, который я имел в виду, чтобы передать новые параметры и использовать этот измененный метод

Но теперь что? У меня есть dozens/hundreds/whoknows, сколько других мест в системе, которая должна быть изменена. На динамическом языке OO как Ruby или JavaScript, как я пошел бы об этом?

Первое, что пришло на ум, не зная Ruby очень хорошо, я могу думать о двух немых ответах:

  1. 100%-е покрытие Кода. Я тестирую целое приложение и каждый раз, когда оно повреждается, я вижу, случается ли так, что метод и фиксирует его
  2. Найти и заменить. Я использую текстовый поиск для поиска того метода. Но у меня могли быть другие объекты с теми же именами методов.

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

c = Customer.new

это смогло бы понять это, но что, если это

c= SomeFunctionThatProbablyReturnsACustomerButMightReturnOtherThings()

Таким образом, какой подход был бы Вы, эксперты Ruby берут в этом случае?

9
задан LoveMeSomeCode 12 August 2010 в 13:33
поделиться

2 ответа

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

Но это только верхушка айсберга. Ruby разработан с учетом определенных рекомендаций, таких как короткие выразительные функции, разделение ответственности на модули, неповторение кода (DRY), принцип наименьшего удивления и т. Д .; плюс набор рекомендуемых практик, таких как сначала тестирование, передача параметров в виде хеш-параметров, разумное использование метапрограммирования и т. д. Я уверен, что другие динамические языки тоже делают это.

Если c не является Клиентом, то, по крайней мере, я ожидаю, что будет действовать как клиент. IDE могут искать утиную типизацию, что более гибко, чем проверка экземпляра определенного класса.

Некоторые IDE (по крайней мере, Rubymine) также обращаются к соглашениям. Например, в приложениях Rails Rubymine переходит к файлу схемы и добавляет свойства модели в базу данных как методы. Он также распознает ассоциации (has_many, own_to и т. Д.) И динамически добавляет соответствующие методы, которые Rails генерирует под капотом.

Теперь это в значительной степени снижает потребность в рефакторинге, по крайней мере, сводит его к минимуму. Но, конечно, не решает . И я не думаю, что это можно решить.

3
ответ дан 5 December 2019 в 01:19
поделиться

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

Пример:

def my_method(options = {})
  if options[:name]
    ...
  end
end

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

Другие варианты могут включать в себя переопределение метода в подклассе для выполнения желаемых функций.

Или, как насчет ...

def get_order_history(required_param, options = [])

  @required_param = required_param

  if options[:do_something_else]
    result = other_method(options[:do_something_else])
  else
    result = ...
  end

  result      

end

def other_method(something_else)
  ...
end
1
ответ дан 5 December 2019 в 01:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: