博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ruby实例方法约束简谈
阅读量:5873 次
发布时间:2019-06-19

本文共 4420 字,大约阅读时间需要 14 分钟。

在Ruby的实例方法中分别有public, private以及protected三种类型,不同类型的方法将会有不一样的访问约束。而这篇文章我主要想详细介绍一下他们之间的区别。

1. public方法

在类词法作用域中直接定义的实例方法都是公有方法。公有方法既可以在类词法作用域内部被其他实例方法调用,也可以在作用域外部创建实例并直接调用,简单举个例子

class Person  def initialize(first_name, last_name)    @first_name = first_name    @last_name = last_name  end  def message    "Hello #{full_name}!!!"  end  def full_name    "#{@first_name} #{@last_name}"  endend@p = Person.new("zhiheng", "Lan")puts @p.full_name # => zhiheng Lanputs @p.message # => Hello zhiheng Lan!!!复制代码

上述定义的Person#messagePerson#full_name都是公有的实例方法。

  1. 我们可以在实例化Person类之后,直接通过实例变量@p来直接调用Person#full_name方法。
  2. 当实例变量@p显式调用Person#message的时候,该方法会在内部调用Person#full_name方法。

2. private方法

许多时候我们并不希望把实例方法暴露到词法作用域之外,被创建的实例直接调用。这个时候就可以考虑把这些方法定义为私有方法。

与Java语言不同,在Ruby中我们并不需要在定义私有方法的时候在每个方法前面加上private关键字,而只需要分配一块私有区域把所有的私有方法都写在这个区域里面。接下来我们把原有的Person#full_name方法设置成私有方法,并添加一个名为Person#greet的私有方法

class Person  def message    "#{greet} #{full_name}!!!"  end  private    def full_name      "#{@first_name} #{@last_name}"    end    def greet      "Hi,"    endend@p = Person.new("zhiheng", "Lan")puts @p.greet#=> NoMethodError: private method `greet' called for #
puts @p.full_name# => NoMethodError: undefined method `full_message' for #
puts @p.message# => Hi, zhiheng Lan!!!复制代码

PS: 对私有区域的代码进行缩进似乎是社区里面常有的做法。

可见,Person#greetPerson#full_name这两个方法我们无法再通过实例直接调用,因为他们已经被私有化了。但是在类词法作用域内部Person#full_name依然可以被Person#message这个实例方法调用。

3. protected方法

保护方法与私有方法之前的区别比较微妙。他们之间的相同点都是不能在类词法作用域之外被实例直接调用,但比起保护方法它的约束更多

在类词法作用域内,我们无法给予私有方法一个确切的接收者。

举个例子,分别定义一个私有方法以及一个保护方法,并在类词法作用域内部通过另外一个实例方法来调用他们

class Person  def call_method    puts protected_method    puts private_method    puts self.protected_method    puts self.private_method # will raise error  end  private    def private_method      "I am private method"    end  protected    def protected_method      "I am protected method"    endend@p = Person.new("zhiheng", "Lan")@p.call_method # => I am protected method # => I am private method # => I am protected method # => NoMethodError: private method `private_method' called for #
复制代码

可见,如果给私有方法添加一个显式的接收者self的话,它就会报错了,而保护方法在这种情况下却可以正常运行,这就是他们之间最大的不同。

再举个例子,添加一个方法Person#==用来判断两个Person实例是否是同一个人(只要Person#full_name相同则认为是同一个人)。

class Person  def ==(other)    self.full_name == other.full_name  endend@lan = Person.new("zhiheng", "Lan")@liang = Person.new("haidao", "Liang")puts @lan == @liang# => NoMethodError: private method `full_name' called for #
复制代码

因为之前的Person#full_name方法被定义成私有的了,所以这个时候显式为它指定接收者self的话就会抛出异常。解决方案就是把它重新定义为一个保护方法

class Person  def ==(other)    self.full_name == other.full_name  end  protected    def full_name      "#{@first_name} #{@last_name}"    endend@lan = Person.new("zhiheng", "Lan")@liang = Person.new("haidao", "Liang")puts @lan == @liang# => false复制代码

比起公有以及私有方法,保护方法的应用范围似乎并不是那么多,但理解他们之间的区别对于我们这些Ruby程序员来说还是很有必要的。

4. 继承关系中的约束现象

在Ruby中,三种类型的实例方法都能够在子类的词法作用域中被直接调用,其行为就像是在子类中也定义了相关的方法。举个简单的例子

class Animal  def initialize(name, age)    @age = age    @name = name  end  def name    @name  end  private  def greet    "hello world"  end  protected  def age    @age  endendclass Cat < Animal  def test    puts name    puts greet    puts age  endendCat.new("tom", 1).test# tom# hello world# 1复制代码

可见,即便是私有的方法Animal#greet,在类被继承后却依然能够在子类的词法作用域中直接调用。接下来我再往Cat类中定义两个实例方法,看些稍微有趣的现象

...class Cat  def private_or_protected    puts greet #4    puts Animal.new('James', 24).age #2    puts Animal.new('James', 24).greet #3  end  private  def greet #5    "hello cat"  endendputs Animal.new('James', 24).age #1# NoMethodError: protected method `age' calledCat.new("tom", 1).private_or_protected# hello cat# 24# NoMethodError: private method `greet' called复制代码

对比#1#2Animal#age作为一个保护方法,虽然无法在顶级作用域中通过实例直接调用,但如果我们是在子类Cat的词法作用域中对Animal进行实例化,便可以直接调用Animal#age方法了。

再对比#2#3,私有方法Animal#greet无法像保护方法Animal#age那样在子类的词法作用域中被父类的实例直接调用,毕竟我们不能够给一个私有方法添加显式的接收者。

代码片段#5可以对父类的Animal#greet这个私有方法进行重写,在片段#4中直接调用。

5. Ruby的私有方法真的有那么私有吗?

Ruby给予了程序员最大的自由度,公私有方法在Ruby中与其说是约束,还不如说是分类。在词法作用域外部,对象通过Object#send方法依然可以直接调用私有方法,例子如下

class Say  private    def hello      "Hello World"    endend@s = Say.new@s.hello# => NoMethodError: private method `hello' called for #
@s.send(:hello)# => "Hello World复制代码

用这种黑科技来调试方法还是很方便的。

你能够做任何事情,但你要你清楚自己在做什么。

6. 尾声

本章主要展示在Ruby里面private, public, protected这三种类型的实例方法之间的区别。后面又谈到Ruby里面的私有方法其实也没那么私有,在必要时我们依然可以通过一些黑科技来直接调用它们。

参考文献

Happy Coding and Writing !!!

转载地址:http://hphnx.baihongyu.com/

你可能感兴趣的文章
.Net 通过MySQLDriverCS操作MySQL
查看>>
JS Cookie
查看>>
ubuntu Unable to locate package sysv-rc-conf
查看>>
笔记:认识.NET平台
查看>>
cocos2d中CCAnimation的使用(cocos2d 1.0以上版本)
查看>>
【吉光片羽】短信验证
查看>>
MacBook如何用Parallels Desktop安装windows7/8
查看>>
gitlab 完整部署实例
查看>>
GNS关于IPS&ASA&PIX&Junos的配置
查看>>
七天学会ASP.NET MVC (四)——用户授权认证问题
查看>>
upgrade to iOS7,how to remove stroyboard?
查看>>
影响企业信息化成败的几点因素
查看>>
SCCM 2016 配置管理系列(Part8)
查看>>
zabbix监控部署
查看>>
关于Tomcat下项目中文名在Windows和Linux下编码混乱问题解决
查看>>
struts中的xwork源码下载地址
查看>>
Android硬件抽象层(HAL)深入剖析(二)
查看>>
CDays–4 习题一至四及相关内容解析。
查看>>
L3.十一.匿名函数和map方法
查看>>
前端自动化构建工具webpack (一)之webpack安装 和 设置webpack.confi.js
查看>>