119

我有一个两部分的问题

最佳实践

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

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

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

技术部分

有什么方法可以制作私有 Module 方法吗?

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

private那里似乎没有任何效果,我仍然可以Thing.priv毫无问题地打电话。

4

10 回答 10

94

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

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
于 2009-01-08T15:11:19.257 回答
83

还有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
于 2009-01-06T21:48:51.953 回答
70
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)
于 2016-01-26T11:15:32.800 回答
29

当一个模块混入时,你可以使用“包含”方法来做一些花哨的事情。这就是你想要的我认为:

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
于 2008-11-26T16:58:34.490 回答
11

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

class << self
  private

  def foo()
   ....
  end
end

诚然,我没有在模块中这样做过。

于 2008-11-25T21:15:25.440 回答
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
于 2015-06-15T20:21:16.613 回答
2

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

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)
于 2020-06-29T06:10:50.810 回答
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.

于 2012-10-17T00:21:35.077 回答
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的文档。

于 2016-11-10T15:56:24.867 回答
0

这是一个解决方案,您可以在单个模块中嵌套多个类,并能够通过使用以下方法调用可从任何嵌套类访问的模块上的私有方法extend

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
于 2021-01-25T23:59:44.023 回答