Частные модульные методы в Ruby
У меня вопрос из двух частей
Лучшая практика
- У меня есть алгоритм, который выполняет некоторую операцию над структурой данных с использованием открытого интерфейса
- В настоящее время это модуль с множеством статических методов, все частные, за исключением одного метода открытого интерфейса.
- Существует одна переменная экземпляра, которая должна быть разделена между всеми методами.
Это варианты, которые я вижу, что лучше?:
- Модуль со статическими ('module' in ruby) методами
- Класс со статическими методами
- Модуль Mixin для включения в структуру данных
- Рефакторинг из части алгоритма, который изменяет эту структуру данных (очень мал) и делает это mixin, который вызывает статические методы модуля алгоритма
Техническая часть
Есть ли способ сделать частный метод модуля?
module Thing
def self.pub; puts "Public method"; end
private
def self.priv; puts "Private method"; end
end
private
там, кажется, не имеет эффекта, я все равно могу вызвать Thing.priv
без проблем.
Ответы
Ответ 1
Я думаю, что лучший способ (и в основном, как написаны существующие библиотеки) сделать это, сделав класс в модуле, который имеет дело со всей логикой, и модуль просто предоставляет удобный метод, например.
module GTranslate
class Translator
def perform( text ); 'hola munda'; end
end
def self.translate( text )
t = Translator.new
t.perform( text )
end
end
Ответ 2
Там также Module.private_class_method
, который, возможно, выражает больше намерений.
module Foo
def self.included(base)
base.instance_eval do
def method_name
# ...
end
private_class_method :method_name
end
end
end
Для кода в вопросе:
module Thing
def self.pub; puts "Public method"; end
def self.priv; puts "Private method"; end
private_class_method :priv
end
Ruby 2.1 или новее:
module Thing
def self.pub; puts "Public method"; end
private_class_method def self.priv; puts "Private method"; end
end
Ответ 3
Вы можете использовать метод "включено", чтобы делать причудливые вещи, когда модуль смешивается. Это касается того, что вы хотите, я думаю:
module Foo
def self.included(base)
class << base
def public_method
puts "public method"
end
def call_private
private_method
end
private
def private_method
puts "private"
end
end
end
end
class Bar
include Foo
end
Bar.public_method
begin
Bar.private_method
rescue
puts "couldn't call private method"
end
Bar.call_private
Ответ 4
module Writer
class << self
def output(s)
puts upcase(s)
end
private
def upcase(s)
s.upcase
end
end
end
Writer.output "Hello World"
# -> HELLO WORLD
Writer.upcase "Hello World"
# -> so.rb:16:in `<main>': private method `upcase' called for Writer:Module (NoMethodError)
Ответ 5
К сожалению, private
применяется только к методам экземпляра. Общий способ получить частные "статические" методы в классе - это сделать что-то вроде:
class << self
private
def foo()
....
end
end
По общему признанию, я не играл с этим в модулях.
Ответ 6
Хороший способ:
module MyModule
class << self
def public_method
# you may call the private method here
tmp = private_method
:public
end
private def private_method
:private
end
end
end
# calling from outside the module
puts MyModule::public_method
Ответ 7
Как насчет хранения методов как lambdas в переменных класса/константах?
module MyModule
@@my_secret_method = lambda {
# ...
}
# ...
end
Для теста:
module A
@@C = lambda{ puts "C" }
def self.B ; puts "B"; @@C[] ; end
private # yeah, this has no sense, just for experiment
def self.D ; puts "D"; @@C[] ; end
end
for expr in %w{ A::B A.B A::C A.C A::D A.D }
eval expr rescue puts expr
end
Здесь мы видим, что C может быть успешно использован B и D, но не снаружи.
Ответ 8
Лучший образец, который я нашел, делая это в Rails, - это отказаться от модулей, которые хотят иметь частные методы и вместо этого использовать класс Singleton. Он не чувствует себя хорошо, но он работает и кажется более чистым, чем другие примеры, которые я видел в этом вопросе.
Хотелось бы услышать другие мнения по этому поводу.
Пример:
ErrorService.notify("Something bad happened")
class ErrorService
include Singleton
class << self
delegate :notify, to: :instance
end
def notify(message, severity: :error)
send_exception_notification(message)
log_message(message, severity)
end
private
def send_exception_notification(message)
# ...
end
def log_message(message, severity)
# ...
end
end
Ответ 9
Сделать частный модуль или класс
Константы никогда не являются частными. Тем не менее, возможно создать модуль или класс, не присваивая его константе.
Таким образом, альтернативой :private_class_method
является создание частного модуля или класса и определение общедоступных методов на нем.
module PublicModule
def self.do_stuff(input)
@private_implementation.do_stuff(input)
end
@private_implementation = Module.new do
def self.do_stuff(input)
input.upcase # or call other methods on module
end
end
end
Использование:
PublicModule.do_stuff("whatever") # => "WHATEVER"
Смотрите документы для Module.new и Class.new.