Частные методы модуля в 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 87

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