回到Ruby系列文章 
 
Ruby设置方法可见性:private、public和protected Ruby中有三种方式可设置实例方法的可见性规则:private(私有)、public(公共)和protected(受保护)。
它们有两种方式设置方法的可见性,以private为例:
private规则 通过private可将实例方法私有化,私有化的方法只允许在当前类(或子类)内部(严格来说是实例方法内)以省略self的无点引用方式来调用 。
私有方法不允许使用obj.meth的方式来调用,连self.meth也不允许(因为在对象上下文,self就等价于obj),所以它不能在外界访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  C  def  f      puts "f"    end    private  :f             def  g      puts "g"      f                     end  end C.new.g 
 
私有方法能在子类中被访问 ,比如puts等『内置』方法就是私有方法,但它们可以在任何地方被调用。这是因为子类继承父类方法的同时还会继承该方法的可见性规则 。所以,父类中的私有方法m,在子类中也是私有方法m,所以子类中也能访问m。但是,子类可以更改某方法的可见性规则,也可以在子类中重新定义方法,这将使得私有方法重新变为public。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  C  def  f      puts "f"    end    private  :f  end class  CC  < C  def  g      puts "g"      f                 end  end CC .new.g
 
子类更改继承自父类的方法可见性规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  C  def  f      puts "f"    end    private  :f  end class  CC  < C  public  :f        def  g      puts "g"      self .f        end  end CC .new.gCC .new.f       
 
不能在同类的其它对象中访问,因为要在其它对象中访问私有方法,总是会使用obj.x的访问方式。这一点主要是为了和protected可见性规则做比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Person   attr_reader  :age       private  :age              def  initialize  name, age     @name  = name     @age  = age   end    def  gt? (other )               @age  > other.age   end  end p1 = Person .new("junmajinlong" , 23 ) p2 = Person .new("gaoxiaofang" , 22 ) p p1.gt? p2 
 
需注意,无点引用setter方法时会和创建同名局部变量冲突,Ruby对这种情况的私有方法调用做了特殊处理:遇到同名局部变量时,允许使用self.x的方式来引用私有方法x。
另外,如果访问私有方法时已存在同名局部变量,则在访问私有方法时必须不能省略括号,否则表示访问局部变量而非私有方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  C  attr_accessor  :age    private  :age=       def  f      puts "in f"    end    private  :f    def  m           self .age = 33        f = 3      f()         end  end c = C.new c.m puts c.age 
 
protected规则 通过protected可将方法保护起来,它的私有性处在public和private的中间:  
在类内部,protected扮演的角色是public,即对内公开(当然,也包括子类)   
在类外部,protected扮演的角色是private,即对外隐藏 
 
所以,受保护的实例方法允许在实例方法的内部访问以任意方式访问(包括无点引用m和self.m和obj.m),但不允许在外界通过obj.m的方式来访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  C  def  f      puts "f"    end    protected  :f    def  g      self .f           f   end  end C.new.g 
 
protected主要是限制外界访问,类内部、子类或同类对象都是自由的。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Person   attr_reader  :age         protected  :age              def  initialize  name, age     @name  = name     @age  = age   end    def  gt? (other )          @age  > other.age   end  end p1 = Person .new("junmajinlong" , 23 ) p2 = Person .new("gaoxiaofang" , 22 ) p p1.gt? p2 
 
public规则 当定义某个对象的实例方法时(除了特殊的initialize方法外),如果不做任何处理,它们默认就是public的方法,即直接暴露给外界,外界可以通过obj.x的方式来调用obj对象的x方法。
1 2 3 4 5 6 7 class  C  def  f      puts "in f"    end  end c = C.new c.f         
 
对于不做任何修改的方法是public的说法,有两个例外:  
1 2 3 4 5 6 7 8 9 10 11 12 def  f ;end      f p self .private_methods.include ? :f    >>  def  f ;end >>  self .f     >>  self .public_methods.include ? :f   
 
通过send()绕过可见性规则 当x方法被设置为private或protected后,外界将不可通过obj.x访问obj对象的x方法。
可通过send()或__send__()来绕过可见性规则,因为这时并不是使用obj.x的方式来访问的。__send__()和send()等价,它只是为了避免某些情况下send被用户自定义为其它方法而提供的。
如果明确想要使用send()时也遵守可见性规则,可使用public_send(),它只会调用public方法,当调用private或protected的方法时将报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  C  private  :f    def  f (arg )     puts "in f: #{arg} "    end    def  g      puts "in g"      self .send :f , "ARG"       end  end c = C.new c.send :f , "ARG"     C.new.g C.new.public_send :f , "ARG"    
 
设置类方法的可见性 Ruby中的类也是对象,类方法实际上是定义在类对象上的(在类对象的单例空间内)。所以,也可以设置类方法的可见性。
因直接使用private等方法设置可见性时,只能设置实例方法,所以要设置类方法(类对象的实例方法)的可见性规则,需进入类的单例空间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  C  def  self .f     puts "in self.f"    end       class  << self                private  :f    end    def  self .g     puts "in self.g"      f        end  end C.g 
 
Ruby额外提供了设置类方法可见性规则的方法,使得无需进入类对象的单例空间。但只提供了私有和公开两种可见性规则设置的方法:  
private_class_method:将类方法私有化   
public_class_method:将类方法公开 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  C  def  self .f     puts "in self.f"    end    private_class_method :f    def  self .g     puts "in self.g"      f        end  end C.g 
 
有时候确实需要将类方法设置为私有方法,比如将类方法new()设置为私有,使之不允许在外界创建该类的实例对象,只允许在类的内部通过无点引用new的方式来创建实例,比如这样可以实现单例类(和前文描述的单例类概念不同,此处的单例类是只允许有一个实例对象的类)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class  C           class  << self      private  :new      def  instance        @instance  = @instance  ? @instance  : new("Admin" )     end    end    def  initialize  name     @name  = name   end  end c1 = C.instance c2 = C.instance p c1 p c2 p c1 === c2 
 
设置常量的可见性 除了方法,还可以设置常量的可见性。同样,常量只有private和public两种可见性规则。
1 2 3 # 可以同时设置多个常量参数 private_constant public_constant 
 
设置私有常量后,将不能使用::的方式去访问,只能通过常量名访问。
1 2 3 4 5 6 7 8 class  C  Version  = '0.1.1'    private_constant  :Version       p Version  end 
 
和private、public、protected相关的方法 在Object和Module中定义了与private、public和protected相关的方法。
Object中定义的方法 1 2 3 4 5 6 7 methods() 返回对象的所有public和protected方法,包括继承链中的方法。加上参数false,只返回对象的单例方法 private_methods protected_methods public_methods 分别返回对象的private、protected、public方法,加上参数false,只返回当前对象方法,不考虑继承链 
 
Object中定义的这些方法的接收者是对象实例obj 。但注意,类或模块也是对象。
例如:
1 2 3 4 5 6 p 33 .private_methods.size p Integer .public_methods.size class  Cend p C.methods.size 
 
Module中定义的方法 Module中定义的这些方法的接收者是类名或模块名 ,而不能是实例对象(在类或模块上下文,可省略receiver)。这意味着它们是在类或模块空间中查找或设置类方法、实例方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # 下面这些前文已解释过 private public protected private_class_method public_class_method private_constant public_constant instance_methods 对于类或模块,返回继承链中所有public和protected方法, 指定参数false,则不考虑继承链 private_instance_methods protected_instance_methods public_instance_methods 分别返回类或模块中定义的私有、公开和受保护的实例方法, 指定参数false,则不考虑Mix-in的模块 method_defined? 只作用于类或模块,判断继承链中的public和protected方法 中是否有指定方法。 指定第二个参数false,则不考虑继承链 private_method_defined? public_method_defined? 只作用于类或模块,分别判断继承链中的private和public方法中 是否有指定方法。 指定第二个参数false,则不考虑继承链 
 
例如,官方手册上给的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module  A  def  method1 ()  end  end class  B  private    def  method2 ()  end  end class  C < B  include  A   def  method3 ()  end  end A.method_defined? :method1                     C.private_method_defined? "method1"            C.private_method_defined? "method2"            C.private_method_defined? "method2" , true      C.private_method_defined? "method2" , false     C.method_defined? "method2"