Ruby中的私有模块方法

时间:2008-11-25 21:01:49

标签: ruby private-methods access-specifier

我有两个问题

最佳实践

  • 我有一个使用公共接口对数据结构执行某些操作的算法
  • 它目前是一个包含许多静态方法的模块,除了一个公共接口方法外都是私有的。
  • 需要在所有方法之间共享一个实例变量。

这些是我能看到的选项,哪个最好?:

    带有静态(ruby中的“模块”)方法的
  • 模块
  • 使用静态方法
  • Mixin 模块以包含在数据结构中
  • 重构输出修改该数据结构的算法部分(非常小)并使其成为调用算法模块静态方法的mixin

技术部分

有没有办法制作私有模块方法

module Thing
  def self.pub; puts "Public method"; end
  private
  def self.priv; puts "Private method"; end
end

private似乎没有任何影响,我仍然可以毫无问题地致电Thing.priv

11 个答案:

答案 0 :(得分:84)

我认为最好的方式(以及主要是如何编写现有的lib)通过在模块中创建一个处理所有逻辑的类来实现这一点,并且模块只是提供了一种方便的方法,例如

module GTranslate
  class Translator
    def perform( text ); 'hola munda'; end
  end

  def self.translate( text )
    t = Translator.new
    t.perform( text )
  end
end

答案 1 :(得分:72)

还有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

答案 2 :(得分:35)

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)

答案 3 :(得分:28)

当混合使用模块时,你可以使用“included”方法做一些奇特的事情。这就是你想要的想法:

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 :(得分:12)

不幸的是,private仅适用于实例方法。在类中获取私有“静态”方法的一般方法是执行以下操作:

class << self
  private

  def foo()
   ....
  end
end

不可否认,我没有在模块中这样做过。

答案 5 :(得分:2)

一个好方法就是这样

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

答案 6 :(得分:2)

The best pattern that I've found by doing this in Rails is to give up on modules that want to have private methods and use a Singleton class instead. It doesn't feel right but it does work and seems cleaner that then other examples I've seen in this question.

Would love to hear other opinions on this.

Example:

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

答案 7 :(得分:1)

在类变量/常量中将方法存储为lambda是什么意思?

module MyModule
  @@my_secret_method = lambda {
    # ...
  }
  # ...
end

进行测试:
UPD:6年后对此代码的大量更新显示了声明私有方法的更简洁方法d

module A
  @@L = lambda{ "@@L" }
  def self.a ; @@L[] ; end
  def self.b ; a ; end

  class << self
    def c ; @@L[] ; end
    private
    def d ; @@L[] ; end
  end
  def self.e ; c ; end
  def self.f ; self.c ; end
  def self.g ; d ; end
  def self.h ; self.d ; end

  private
  def self.i ; @@L[] ; end
  class << self
    def j ; @@L[] ; end
  end

  public
  def self.k ; i ; end
  def self.l ; self.i ; end
  def self.m ; j ; end
  def self.n ; self.j ; end
end

for expr in %w{ A.a A.b A.c A.d A.e A.f A.g A.h A.i A.j A.k A.l A.m A.n }
  puts "#{expr} => #{begin ; eval expr ; rescue => e ; e ; end}"
end

我们在这里看到:

A.a => @@L
A.b => @@L
A.c => @@L
A.d => private method `d' called for A:Module
A.e => @@L
A.f => @@L
A.g => @@L
A.h => private method `d' called for A:Module
A.i => @@L
A.j => @@L
A.k => @@L
A.l => @@L
A.m => @@L
A.n => @@L

1)@@L不能从外部访问,但几乎可以从任何地方访问 2)class << self ; private ; def成功地使方法d无法从外部和内部使用self.,但不是没有它 - 这很奇怪 3)private ; self.private ; class << self不会将方法设为私有 - 无论是否有self.

都可以访问这些方法

答案 8 :(得分:0)

创建私有模块或类

常量永远不会私有。但是,可以创建模块或类而不将其分配给常量。

因此: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.newClass.new的文档。

答案 9 :(得分:0)

除非您通过方法参数显式传递数据,否则该方法将不允许与私有方法共享数据。

module Thing
  extend self

  def pub
    puts priv(123)
  end

  private
  
  def priv(value)
    puts "Private method with value #{value}"
  end
end

Thing.pub
# "Private method with value 123"

Thing.priv
# NoMethodError (private method `priv' called for Thing:Module)

答案 10 :(得分:0)

这里有一个解决方案,说明如何将多个类嵌套在单个模块中,并能够通过使用 extend 调用模块上的私有方法,该方法可从任何嵌套类访问:< /p>

module SomeModule

  class ClassThatDoesNotExtendTheModule
    class << self
      def random_class_method
        private_class_on_module
      end
    end
  end

  class ClassThatDoesExtendTheModule
    extend SomeModule
  
    class << self
      def random_class_method
        private_class_on_module
      end
    end
  end

  class AnotherClassThatDoesExtendTheModule
    extend SomeModule
  
    class << self
      def random_class_method
        private_class_on_module
      end
    end
  end

  private

  def private_class_on_module
    puts 'some private class was called'
  end
  
end

显示解决方案的一些输出:

> SomeModule::ClassThatDoesNotExtendTheModule.random_class_method

NameError: undefined local variable or method `private_class_on_module' for SomeModule::ClassThatDoesNotExtendTheModule:Class


> SomeModule::ClassThatDoesExtendTheModule.random_class_method

some private class was called


> SomeModule::ClassThatDoesExtendTheModule.private_class_on_module

NoMethodError: private method `private_class_on_module' called for SomeModule::ClassThatDoesExtendTheModule:Class


> SomeModule::AnotherClassThatDoesExtendTheModule.random_class_method

some private class was called


> SomeModule::AnotherClassThatDoesExtendTheModule.random_class_method

NoMethodError: private method `private_class_on_module' called for SomeModule::AnotherClassThatDoesExtendTheModule:Class