Является ли "расширять себя" анти-шаблоном для служебных модулей? - программирование
Подтвердить что ты не робот

Является ли "расширять себя" анти-шаблоном для служебных модулей?

Стив Клабник недавно сказал в тянуть запрос для служебного модуля:

[Код] скрывает тот факт, что это методы класса, и мы хотят использовать их таким образом. Плюс, я думаю, что extend self вообще анти-шаблон, и его не следует использовать, за исключением некоторых случаев. я подумал об этом, и я думаю, что это один из тех случаев.

  • При создании служебного модуля (например, для математики), что лучший способ объявить методы?
  • И когда подходит extend self идиома?
4b9b3361

Ответ 1

Не совсем полезно, что он не говорит: 1) почему он считает, что это анти-шаблон (за исключением того, что вы определяете тот факт, что вы определяете методы класса) или 2) почему, подумав об этом, это одно случаев, которые, по его мнению, не должны использоваться, поэтому трудно конкретно противостоять любым из его аргументов.

Однако я не уверен, что extend self является анти-шаблоном. Компонент полезности является хорошим примером использования для него. Я также использовал его как удобное хранилище для тестовых приборов.

Я думаю, что стоит рассмотреть, что такое extend self, какие могут быть проблемы с ним и какие существуют альтернативы.

В этом ядро ​​это просто способ избежать необходимости писать self. перед каждым определением метода в модуле, который вы никогда не намерены смешивать в класс, и поэтому у него никогда не будет "собственного" экземпляра, поэтому по определению могут иметь только "классные" методы (если вы хотите иметь возможность их называть, то есть).

Не скрывает ли тот факт, что вы намерены использовать методы как методы класса? Ну, да, если вы не смотрите вверху файла туда, где он говорит extend self, это возможно. Тем не менее, я бы сказал, что если это возможно для вас, чтобы сделать эту путаницу, ваш класс, вероятно, слишком сложный.

Из вашего класса должно быть очевидно - от его имени и от его содержимого - что он предназначен как набор функций полезности. В идеале это было бы не намного больше, чем экран, так что extend self почти никогда не исчезнет из виду. И, как мы увидим, альтернативы также страдают почти по той же проблеме.

Одним из вариантов было бы использовать class << self следующим образом:

module Utility
  class << self
    def utility_function1
    end
    def utility_function2
    end
  end
end

Я не поклонник этого, не в последнюю очередь потому, что он вводит дополнительный слой отступов. Это тоже уродливое (полностью субъективное, я знаю). Он также страдает от одной и той же проблемы "затушевывания" того факта, что вы определяете методы класса.

Вы также можете использовать этот подход для определения методов экземпляра вне блока class << self, что может привести к искушению сделать это (хотя я надеюсь, что это не так), поэтому я бы сказал что extend self превосходит в этом отношении, устраняя возможность этой путаницы вод.

(То же самое верно, конечно, из "длинного" стиля использования def self.utility_function.)

Другим подходом может быть использование одноэлементного объекта. Я не думаю, что это хорошая идея вообще, потому что одноэлементный объект является объектом по какой-то причине - он предназначен для того, чтобы удерживать состояние и делать что-то, но также быть единственным, что существует. Это просто не имеет смысла для модуля полезности, который должен быть серией независимых функций без гражданства. Вы не хотите, чтобы MathUtils.cos(90) возвращал другое значение, основанное на внутреннем состоянии MathUtils, правильно? (Я знаю, что вы, конечно, можете держать состояние в модуле и делать все это, но для меня это скорее семантическое разделение, чем техническое).

Это также приводит к той же проблеме, которая, возможно, скрывает тот факт, что методы призваны называться методами класса (вроде). Они определяются как методы экземпляра, и вы называете их как методы экземпляра, но сначала получаете один экземпляр класса, вызывая метод класса instance.

class MathSingleton
  include Singleton

  def cos x
  end
end

MathSingleton.instance.cos x

Это было бы ужасной альтернативой extend self для этой цели. Но посмотрите, кроме того, единственное, что указывает на то, что эти методы должны использоваться как методы для экземпляра singleton, состоит в том, что одна строка находится вверху, точно так же, как extend self.

Итак, какие другие возможные недостатки? Я ничего не знаю, но мне было бы интересно услышать их, если кто-то еще сделает.

Я бы сказал, что extend self приводит к более короткому коду, который оставляет вне постороннего self. и позволяет сосредоточиться на именах и, следовательно, на значениях его методов.

У него также есть приятное свойство, которое, например, если вы пишете другой класс, который использует множество функций вашей полезности, вы можете просто его смешивать, и они будут доступны без использования имени модуля каждый раз. Подобно тому, как статический импорт работает на других языках.

Ответ 2

Утилитные модули, в отличие от mixins, представляют собой контейнеры, которые обертывают константы и методы с некоторой общей проблемой. Микшины, такие как этот,

module SingingCapability
  def sing; puts "I'm singing!" end
end

Human = Class.new
Fred = Human.new.tap { |o| o.extend SingingCapability }

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

В целом, я думаю, что это несколько не очень хорошая практика, но Ruby бросает вызов мне на этом, так как у нас даже есть метод Module#module_function для облегчения этой халатности:

module SingingBox
  def sing; "Tralala!" end
  module_function :sing
end

SingingBox.sing #=> "Tralala!"