Частные методы модуля в Ruby
у меня есть вопрос из двух частей
Лучшие Практики
- у меня есть алгоритм, который выполняет некоторые операции над структурой данных через публичный интерфейс
- в настоящее время модуль с многочисленными статическими методами, все частные, за исключением одного метода публичного интерфейса.
- есть одна переменная экземпляра, которая должна быть общей для всех методов.
вот варианты, которые я вижу, что лучше?:
- модуль со статическими ('модуль' в ruby) методами
- класс со статическими методами
- Mixin модуль для включения в структуру данных
- рефакторинг из части алгоритма, который изменяет эту структуру данных (очень маленький) и сделать это mixin, который вызывает статические методы алгоритма модуль
технические детали
есть ли способ сделать метод частного модуля?
module Thing
def self.pub; puts "Public method"; end
private
def self.priv; puts "Private method"; end
end
The private
там, кажется, не имеет никакого эффекта, Я все еще могу называть Thing.priv
без проблем.
9 ответов:
Я думаю, что лучший способ (и в основном, как пишутся существующие библиотеки) сделать это, сделав класс в модуле, который имеет дело со всей логикой, и модуль просто предоставляет удобный метод, например
module GTranslate class Translator def perform( text ); 'hola munda'; end end def self.translate( text ) t = Translator.new t.perform( text ) end end
там же
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
вы можете использовать "включенный" метод, чтобы делать причудливые вещи, когда модуль смешивается. Это делает о том, что вы хотите, я думаю:
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
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)
к сожалению,
private
применяется только к методам экземпляра. Общий способ получить частные "статические" методы в классе-это сделать что-то вроде:class << self private def foo() .... end end
правда, я не играл с этим в модулях.
хороший способ, как это
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
лучший шаблон, который я нашел, делая это в Rails-это отказаться от модулей, которые хотят иметь частные методы и использовать одноэлементный класс. Это не кажется правильным, но это работает и кажется более чистым, чем другие примеры, которые я видел в этом вопросе.
хотелось бы услышать другие мнения на этот счет.
пример:
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
сделать частный модуль или класс
константы никогда не являются частными. Однако, можно создать модуль или класс, не назначая его к постоянному.
так что альтернативы
: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 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