从私有实例方法调用私有类方法

And*_*zub 6 ruby oop

我是Ruby新手,来自C#世界.在C#中,做这样的事情是合法的:

public class Test
{
  public void Method()
  {
     PrivateMethod();
  }

  private void PrivateMethod()
  {
     PrivateStaticMethod();
  }

  private static void PrivateStaticMethod()
  {
  }
}
Run Code Online (Sandbox Code Playgroud)

可以在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
Run Code Online (Sandbox Code Playgroud)

我试图将我的私有方法转换为私有类方法,但我仍然得到一个错误:

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
Run Code Online (Sandbox Code Playgroud)

ahm*_*hmy 5

首先让我尝试解释为什么代码不起作用

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
  ...
Run Code Online (Sandbox Code Playgroud)

您可以使用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
Run Code Online (Sandbox Code Playgroud)

现在你可以用这个对象说这样的话

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
Run Code Online (Sandbox Code Playgroud)

initialize_instance如果您认为有必要,您可能还想移动

一些深入研究这种模式的资源: