Ruby 中的私有模块方法

我有两个问题

Best-Practice

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

这些是我能看到的选择,哪一个是最好的:

  • Module 使用 static (Ruby 中的‘ Module’)方法
  • 使用静态方法创建
  • Mixin 模块用于包含到数据结构中
  • Refactor out the part of the algorithm that modifies that data structure (very small) and make that a mixin that calls the static methods of the algorithm module

技术部分

有没有办法做一个 私有模块方法私有模块方法

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

里面的 private似乎没有什么效果,我还可以调用 Thing.priv没有问题。

96962 次浏览

遗憾的是,private只适用于实例方法。在类中获得私有“静态”方法的一般方法是这样做:

class << self
private


def foo()
....
end
end

必须承认,我还没有在模块中尝试过这样做。

当一个模块混合在一起时,您可以使用“ include”方法来完成一些奇特的事情。这就是你想要的,我想:

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

There's also Module.private_class_method, which arguably expresses more intent.

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

在类变量/常量中将方法存储为 lambdas 怎么样?

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

For test:
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.进行访问,但是没有 self.就不行——这很奇怪
3) private ; self.private ; class << self不使方法私有化——它们可以在有或没有 self.的情况下访问

A nice way is like this

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
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_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"

See the docs for 模块,新的 and 同学们,新来的.

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

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)

这里有一个解决方案,可以让多个类嵌套在一个模块中,通过使用 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