我是Ruby新手,来自C#世界。在C#中,做这样的事情是合法的:
public class Test
{
public void Method()
{
PrivateMethod();
}
private void PrivateMethod()
{
PrivateStaticMethod();
}
private static void PrivateStaticMethod()
{
}
}
是否有可能在Ruby中做类似的事情?
一点上下文:我有一个Rails应用程序......其中一个模型有一个私有方法来设置一些依赖项。有一个类方法可以创建模型的初始化实例。由于遗留原因,有些模型的实例未正确初始化。我添加了一个实例方法,初始化'未初始化'实例,我想要做同样的初始化逻辑。有没有办法避免重复?
样品:
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
model.init_some_dependencies # this fails
model
end
def initialize_instance
// do some other work
other_init
// call private method
init_some_dependencies
end
private
def init_some_dependencies
end
end
我试图将我的私有方法转换为私有类方法,但我仍然收到错误:
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
MyModel.init_some_dependencies_class(model)
model
end
def initialize_instance
# do some other work
other_init
# call private method
init_some_dependencies
end
private
def init_some_dependencies
MyModel.init_some_dependencies_class(self) # now this fails with exception
end
def self.init_some_dependencies_class(model)
# do something with model
end
private_class_method :init_some_dependencies_class
end
答案 0 :(得分:4)
首先让我试着解释为什么代码不起作用
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
# in here, you are not inside of the instance scope, you are outside of the object
# so calling model.somemething can only access public method of the object.
model.init_some_dependencies
...
end
...
您可以使用model.send :init_some_dependencies
绕过对方法的私人调用。但我认为在这种情况下可能有更好的解决方案。
我猜测init_some_dependencies
可能包含更多业务/域逻辑而不是持久性。这就是为什么我建议将这个逻辑拉入“域对象”(或者称之为服务对象)。这只是一个包含域逻辑的普通ruby对象。
这样,您可以将持久性逻辑与ActiveRecord分离,并将域逻辑分离到该类。因此,您不会膨胀ActiveRecord模型。而且你获得了测试奖金 域逻辑不需要ActiveRecord。这将使您的测试更快。
您可以创建一个名为`lib / MyModelDomain.rb'的文件
class MyModelDomain
attr_accessor :my_model
def initialize(my_model)
@my_model = my_model
end
def init_some_dependencies
my_model.property = 'some value example'
end
end
现在您可以使用此对象说出类似这样的内容
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
domain = MyModelDomain.new(model)
domain.init_some_dependencies
domain.my_model
end
def initialize_instance
// do some other work
other_init
domain = MyModelDomain.new(self)
domain.init_some_dependencies
end
end
如果您认为有必要,您可能还想移动initialize_instance
深入研究这种模式的一些资源:
答案 1 :(得分:1)
您可以使用
model = MyModel.new
model.send :init_some_dependencies
绕过方法可见性检查。
答案 2 :(得分:-1)
在C#中,做这样的事情是合法的:
public class Test { public void Method() { PrivateMethod(); } private void PrivateMethod() { PrivateStaticMethod(); } private static void PrivateStaticMethod() { } }
是否有可能在Ruby中做类似的事情?
是:
class Test
def method
private_method()
end
def self.greet
puts 'Hi'
end
private_class_method :greet
private
def private_method
self.class.class_eval do
greet
end
end
end
Test.new.method
Test.greet
--output:--
Hi
1.rb:23:in `<main>': private method `greet' called for Test:Class (NoMethodError)
但红宝石并没有严格执行隐私。例如,
class Dog
def initialize
@private = "secret password"
end
end
puts Dog.new.instance_variable_get(:@private)
--output:--
secret password
ruby让您可以通过一些额外的努力获得访问私人物品的自由:
Test.new.method
Test.class_eval do
greet
end
--output:--
Hi
Hi
在ruby中,私有方法仅表示您无法为方法明确指定接收方,即方法左侧不能有名称和点。但是在ruby中,没有接收器的方法隐含地使用self作为接收器。因此,要调用私有方法,您只需创建一个self是正确接收者的上下文。 class_eval和instance_eval都将块内的self更改为他们的接收者,例如
some_obj.instance_eval do
#Inside here, self=some_obj
#Go crazy and call private methods defined in some_obj's class here
end
您可以将这些规则应用于这种情况:
(ahmy wrote:)
First let me try to explain why the code does not work
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
# in here, you are not inside of the instance scope, you are outside of the object
# so calling model.somemething can only access public method of the object.
model.init_some_dependencies # this fails
... end ...
“上下文”和“范围” - 令人头疼。您需要记住的是:您无法使用显式接收器调用私有方法。 init_some_dependencies方法被定义为私有方法 - 但它具有“模型”。写在它的左边。那是一个明确的接收者。砰!一个错误。
这是一个解决方案:
class MyModel
def self.create_instance
#In here, self=MyModel
puts self
model = MyModel.new
model.instance_eval do #Changes self to model inside the block
#In here, self=model
init_some_dependencies #Implicitly uses self as the receiver, so that line is equivalent to model.init_some_dependencies
end
end
private
def init_some_dependencies
puts "Dependencies have been initialized!"
end
end
MyModel.create_instance
--output:--
MyModel
Dependencies have been initialized!
或者正如ahmy和LBg指出的那样,你可以使用Object#send()来调用私有方法:
class MyModel
def self.create_instance
model = MyModel.new
model.send(:init_some_dependencies, 10, 20)
end
private
def init_some_dependencies(*args)
puts "Dependencies have been initialized with: #{args}!"
end
end
MyModel.create_instance
--output:--
Dependencies have been initialized with: [10, 20]!
答案 3 :(得分:-3)
当然,确实如此。
Ruby的一些OO策略(private
&amp; public
关键字等)来自C ++,因此您可以获得几乎相同的用法。