Я думаю, что есть еще одно преимущество использования внутренних методов, которое заключается в ясности.Подумайте об этом: класс со списком методов - это плоский, неструктурированный список методов.Если вы заботитесь о разделении интересов и сохранении содержимого на одном уровне абстракции, а фрагмент кода используется только в одном месте, внутренние методы приходят на помощь, хотя и намекают на то, что они используются только во вложенном методе.
Предположим, у вас есть этот метод в классе:
class Scoring
# other code
def score(dice)
same, rest = split_dice(dice)
set_score = if same.empty?
0
else
die = same.keys.first
case die
when 1
1000
else
100 * die
end
end
set_score + rest.map { |die, count| count * single_die_score(die) }.sum
end
# other code
end
Теперь, это своего рода простое преобразование структуры данных и более высокоуровневый код, добавление множества кубиков, образующих набор, и тех, которые не принадлежатзадавать.Но не очень понятно, что происходит.Давайте сделаем это более наглядным.Далее следует простой рефакторинг:
class Scoring
# other methods...
def score(dice)
same, rest = split_dice(dice)
set_score = same.empty? ? 0 : get_set_score(same)
set_score + get_rest_score(rest)
end
def get_set_score(dice)
die = dice.keys.first
case die
when 1
1000
else
100 * die
end
end
def get_rest_score(dice)
dice.map { |die, count| count * single_die_score(die) }.sum
end
# other code...
end
Идея get_set_score () и get_rest_score () состоит в том, чтобы документировать с помощью описательного (хотя и не очень хорошего в этом придуманном примере) действия этих частей.Но если у вас есть много таких методов, код в Score () не так легко следовать, и если вы реорганизуете один из методов, вам может понадобиться проверить, какие другие методы их используют (даже если они закрытые - другиеметоды одного и того же класса могут их использовать).
Вместо этого я начинаю отдавать предпочтение таку:
class Scoring
# other code
def score(dice)
def get_set_score(dice)
die = dice.keys.first
case die
when 1
1000
else
100 * die
end
end
def get_rest_score(dice)
dice.map { |die, count| count * single_die_score(die) }.sum
end
same, rest = split_dice(dice)
set_score = same.empty? ? 0 : get_set_score(same)
set_score + get_rest_score(rest)
end
# other code
end
Здесь должно быть более очевидно, что get_rest_score () и get_set_score ()заключены в методы, позволяющие поддерживать логику Score () на том же уровне абстракции, не вмешиваться в хэши и т. д.
Обратите внимание, что технически вы можете вызвать Scoring # get_set_score и Scoring # get_rest_score,но в этом случае это будет плохим стилем IMO, потому что семантически они являются просто приватными методами для одиночного метода score ()
Итак, имея эту структуру, вы всегда сможете прочитать всю реализацию Score () безищем любой другой метод, определенный вне Scoring # score.Несмотря на то, что я не вижу такого кода Ruby часто, я думаю, что я собираюсь преобразовать больше в этот структурированный стиль с помощью внутренних методов.
NOTE : еще один вариант, который не выглядит«Чистый, но избегающий проблемы конфликтов имен» - это просто использовать лямбды, которые были в Ruby с самого начала.Используя пример, он превратится в
get_rest_score = -> (dice) do
dice.map { |die, count| count * single_die_score(die) }.sum
end
...
set_score + get_rest_score.call(rest)
Это не так красиво - кто-то, глядя на код, может задаться вопросом, зачем все эти лямбды, тогда как использование внутренних методов довольно самодокументировано.Я бы все-таки больше склонялся к лямбдам, поскольку у них нет проблемы утечки потенциально конфликтующих имен в текущую область.