Rails 中的受保护和私有方法

Ruby (公共、受保护和私有方法)中的方法可见性在 这篇博文中已经得到了很好的解释。但是在 Ruby on Rails 中,由于框架的设置方式,它似乎与普通 Ruby 应用程序略有不同。那么,在 Rails 模型、控制器、助手、测试等中,什么时候/什么时候不适合使用受保护的或私有的方法呢?

编辑 : 谢谢到目前为止的回答。我理解 Ruby 中 protected 和 private 的概念,但是我更希望了解在 Rails 应用程序的各个部分(模型、控制器、助手、测试)的上下文中使用这些可见性类型的典型方式。例如,公共控制器方法是操作方法,应用程序控制器中的受保护方法用于需要由多个控制器访问的“辅助方法”,等等。

94368 次浏览

The difference between protected and private is subtle. If a method is protected, it may be called by any instance of the defining class or its subclasses. If a method is private, it may be called only within the context of the calling object---it is never possible to access another object instance's private methods directly, even if the object is of the same class as the caller. For protected methods, they are accessible from objects of the same class (or children).

http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Declaring_Visibility

You use a private method if you want no one else but self to use a method. You use a protected method if you want something only self and is_a?(self) s can call.

A good use of protected might be if you had a "virtual" initialization method.

class Base
def initialize()
set_defaults()
#other stuff
end


protected
def set_defaults()
# defaults for this type
@foo = 7
calculate_and_set_baz()
end


private
def calculate_and_set_baz()
@baz = "Something that only base classes have like a file handle or resource"
end
end


class Derived < Base
protected
def set_defaults()
@foo = 13
end
end

@foo will have different values. and the Derived instances will not have @baz

Update: Since I wrote this, some things have changed in Ruby 2.0+ Aaron Patterson has an excellent write up http://tenderlovemaking.com/2012/09/07/protected-methods-and-ruby-2-0.html

You seem to have a good idea of the semantics of class visibility (public/protected/private) as applied to methods. All I can offer is a quick outline of the way I implement it in my Rails apps.

I implement protected methods in the base application controller so they can get called by any controller via filters (e.g. before_filter :method_foo). In a similar way, I define protected methods for models that I want to use in all of them in a base model that they all inherit from.

For models, the idea is that the public methods are the public interface of the class. Public methods are intended to be used by other objects, while protected/private methods are to be hidden from the outside.

This is the same practice as in other object-oriented languages.

For controllers and tests, just do as you please. Both controller and test classes are only instantiated and called by the framework (yes, I know you can theoretically get the controller from the view, but if you do that, something is strange anyway). Since no one will ever create those things directly, there's nothing to "protect" against.

Addendum/Correction: For controllers, you should mark the "helper" methods as protected private, and only the actions themselves should be public. The framework will never route any incoming HTTP calls to actions/methods that are not public, so your helper methods should be protected in that way.

For helpers it will make no difference if a method is protected or private, since they are always called "directly".

You can mark stuff protected in all those cases if it makes things easier for you to understand, of course.

Although actions need to be public methods of a controller, not all public methods are necessarily actions. You can use hide_action if you're using a catch-all route like /:controller/:action/:id or if it's disabled (the default in Rails 3) then only methods with explicit routes will be called.

This can be useful if you're passing the controller instance to some other library like the Liquid template engine as you can provide a public interface rather than having to use send in your Liquid filters and tags.