Ruby中的私有模块方法


108

我有一个两部分的问题

最佳实践

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

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

  • 模块与静态(红宝石“模块”)的方法
  • 带有静态方法的
  • Mixin模块,用于包含在数据结构中
  • 重构算法中修改该数据结构的部分(非常小),并使之混合,从而调用算法模块的静态方法

技术部分

有什么方法可以使私有Module方法吗?

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

private在那里似乎没有任何效果,我仍然可以调用Thing.priv没有问题。


5
仅供参考,在ruby中没有所谓的“静态”方法,它们被称为类实例方法
布拉德(Brad)

31
一个古老的评论,但由于它有四个缺点,我必须指出,没有“类实例方法”之类的东西。“类方法”是正确的术语。
micapam

5
private仅影响实例方法,不影响类方法。使用private_class_method来代替:module Thing; def self.pub; end; private_class_method :pub; end
apeiros

1
@micapam类实例方法确实存在于Ruby中,并且与类方法不同。
Marnen Laibow-Koser

Answers:


88

我认为最好的方法(主要是现有库的编写方式)是通过在模块中创建一个处理所有逻辑的类,而该模块仅提供了一种方便的方法,例如

module GTranslate
  class Translator
    def perform( text ); translate( text ); end

    private

    def translate( text )
      # do some private stuff here
    end
  end

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

14
Ruby newb在这里。在此示例中,Translator类是否公开为模块公共接口的一部分?'perform'方法能否将访问权限限制为GTranslate?
rshepherd 2011年

2
@rshepherd这里perform不是应该私有的方法,私有方法是类中的私有方法Translator(@ucron的示例没有任何东西,很不幸)。GTranslate.translate只是一个方便的方法GTranslate::Translator#perform,如果有可能,没有任何隐藏的实际收益。
michelpm

28
我不确定在这里上课能取得什么成就。如果目标是拥有私有模块方法,则这不符合目标。因为您可以通过调用GTranslate :: Translator.new.perform从模块外部访问“ perform”方法。换句话说,它不是私有的。
Zack Xu

1
@jschorr我认为Op和此答案旨在创建私有类或模块方法,而不是实例方法。而且,不会像self.translate声明类/模块方法那样将任何实例方法设为私有。
konsolebox 2014年

5
GTranslate::Translator.new.perform(text)-令人费解,但不是私密的!
abhillman

80

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

我没有意识到这一点。它也会在方法定义之前起作用private吗?
Marnen Laibow-Koser 2011年

7
此答案与@JCooper的答案一起是真正的解决方案。@ MarnenLaibow-Koser没有。您可以考虑其他答案,但需要更多的分组和缩进。实际上,它可能是某些人的首选解决方案。(仅出于参考目的而答复。)
konsolebox 2014年

58
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)

4
这应该是公认的答案。我认为干净整洁。
马丁·尼亚加

@MartinNyaga这有没有include Writer选择的缺点!
Ulysse BN

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

5
那很聪明。因此有可能,但可能不值得。
丹尼尔·比尔兹利

效果很好。我用的included do |base| [...] end不是def
Crystark

5
@Crystark:该语法仅在扩展ActiveSupport :: Concern的模块上存在,如果我没有记错的话。即这是一个滑轨的东西。
Vaz

11

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

class << self
  private

  def foo()
   ....
  end
end

诚然,我还没有在模块中进行此操作。


7
这不是真的。您可以具有私有类方法和私有模块方法。
mikeycgto 2012年

您可以有私有类方法,但是这样做不会成为.foo私有类方法:“ private; def self.foo()”
Ari

@mikeycgto想要详细说明私有类方法和私有模块方法之间的区别吗?因为我认为它们是相同的。请注意,private和和private_class_method均为Modulenot 所拥有Class。这段代码是通过方式工作的,它是使用的替代方法private_class_method
konsolebox 2014年

3

不错的方法是这样的

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

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.


Lambda与方法完全不同。Lambda是类型Proc,而方法是类型Method
迈克尔·多斯特

1
全局变量不好
激进

@achempion,您在哪里看到他们?
Nakilon

@Nakilon抱歉,如果您希望我取消我的投票,请编辑您的答案
13年

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的文档。


我真的很喜欢这种方法。但是似乎不可能删除.self方法定义中的,将其包含在另一个类中,并将它们用作包含类的instance_method。您知道有什么方法可以使它起作用吗?
Shiyason

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.