Ответ 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.
и позволяет сосредоточиться на именах и, следовательно, на значениях его методов.
У него также есть приятное свойство, которое, например, если вы пишете другой класс, который использует множество функций вашей полезности, вы можете просто его смешивать, и они будут доступны без использования имени модуля каждый раз. Подобно тому, как статический импорт работает на других языках.