Ruby 中的一些基本概念

liuxingqi bio photo By liuxingqi Comment

Public VS Protected VS Private

  1. Public 方法可以在任何地方被调用,没有任何限制。方法默认是 public 的,但有两个例外,initialize 方法是 private 的, 在类外定义的“全局”方法也是Object对象的私有方法;
  2. Private 方法是实现一个类时使用的内部方法。它只能被这个类或子类的实例方法所调用。它不能被一个显式的接收者所调用,只能被 self所隐式调用。这就意味着私有方法只能在当前对象的的上下文中被调用,你不能调用另外一个对象的私有方法。类的私有方法可以被子类继承,子类可以调用 并覆盖父类的私有方法;
  3. Protected 方法与私有方法的相似之处是它也只能在该类或子类的内部被调用,不同之处在于它可以被该类的实例显式调用。

根据实例来分析:

class A
  def test1
    test4
  end

  def test2
    self.test4
  end

  def test3
    A.new.test4
  end

  private
    def test4
      puts "hello, world!"
    end

    def self.test5
      puts "hello, Jason!"
    end
end

a = A.new
a.test1 => hello, world!
#私有方法可以在当前对象的上下文中被调用

a.test2 => NoMethodError: private method `test4' called for #<A:0x007f80b9144c40>
#私有方法不能被显式的接收者调用,即使接收者是self

a.test3 => NoMethodError: private method `test4' called for #<A:0x007f80b914c508>
#私有方法不能被显式的接收者调用,更不能调用其他对象的私有方法

a.test4 => NoMethodError: private method `test4' called for #<A:0x007f80b9144c40>
#同上,私有方法不能被显式的接收者调用

A.test5 => hello, Jason!
#private只作用于实例方法,对类方法是不生效的,如果要让类方法称为私有的,应当使用 private_class_method

如果在此处把 private 改成 protected,让我们再来看看结果是什么样的:

class A
  def test1
    test4
  end

  def test2
    self.test4
  end

  def test3
    A.new.test4
  end

  protected
    def test4
      puts "hello, world!"
    end

    def self.test5
      puts "hello, Jason!"
    end
end

a = A.new
a.test1 => hello, world!
#protected方法可以在当前对象的上下文中被调用

a.test2 => NoMethodError: private method `test4' called for #<A:0x007f80b9144c40>
#protected方法在方法定义中可以被显式的接收者self调用

a.test3 => NoMethodError: private method `test4' called for #<A:0x007f80b914c508>
#protected方法可以被属于同一类或子类的其他对象所调用

a.test4 => NoMethodError: private method `test4' called for #<A:0x007f80b9144c40>
#protected方法在类定义外不能被显式的接收者调用

A.test5 => hello, Jason!
#protected只作用于实例方法,对类方法是不生效的

从上面的例子可以看出,private 和 protected 的区别并不是很大,它们都不能在类定义外被显式的接收者 所调用,但在方法定义中可以被调用,只是在方法定义中,private 也不能被显式调用,但是 protected 可以, 并且 protected 可以被属于同一类或子类的其他对象所调用,但 private 却不可以。

Require VS Load

Require

require.rb:

def test
    puts "hello, world"
end
test

test.require.rb:

puts require "./require.rb"
puts require './require.rb'

output:

hello, world
true
false
Load

load.rb:

def test
    puts "hello, world"
end
test

test.require.rb:

puts load "./load.rb"
puts load "./load.rb"

output:

hello, world
true
hello, world
true

从上面两个例子可以看出,require 和 load 的区别是:如果多次加载同一文件,require 只加载一次,而 load 会加载多次。

Include VS Extend

定义一个 module A, class Test1, class Test2:

module A
  def test
     puts "hello,world!"
  end
end

class Test1
  include A
end

class Test2
  extend A
end

output:

Test1.test => NoMethodError: private method `test' called for Test1:Class
Test1.new.test => hello,world
Test2.test => hello,world
Test2.new.test => NoMethodError: private method `test' called for #<Test2:0x007fa44287bf98>

因此,我们可以看出,如果在一个类中 include 一个模块,模块中的方法将成为类的实例方法,如果 extend 一个 模块,模块中的方法将成为类的类方法。

我们再定义一个 module B, class Test3:

module B
  def test
    puts "hello Jason"
  end
end

class Test3
  include A
  include B
end

Test3.new.test => hello Jason

如果类中 include 了多个模块,并且模块中有相同的方法,后加载的会覆盖之前加载的方法。

Dup VS Clone

这两个方法分配一个调用者所属的实例,然后把调用者的所有实例变量及修改都拷贝到新创建的对象中。 dup 和 clone 用的都是浅拷贝,即如果实例变量包含对其他对象的引用,那么拷贝的是引用而不是对象本身。 它们的区别是:

  • clone 比 dup 拷贝更彻底,它还拷贝调用者的单键方法。
class A
end

a = A.new

def a.play
   puts "play"
end

a.play => "play"
b = a.clone
b.play => "play"
c = a.dup
c.play => "undefined method `play' for #<A:0x007fcbb184eed0> (NoMethodError)"

单键方法是指只对单个对象生效的方法,类方法是特殊的单键方法

  • clone 保留对象 frozen 状态, 而 dup 不会
class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze # 防止对象被修改

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # RuntimeError: can't modify frozen Foo
comments powered by Disqus