Ruby OOP

构造实例

1
2
song1 = Song.new("Ruby Tuesday")
song2 = Song.new("Enveloped in Python")

对象变量

默认赋值操作,都是引用赋值。

对象克隆,object.dup

例子:

1
2
3
4
5
6
7
8
person1 = "Tim"
person2 = person1.dup
person1[0] = "J"
puts "person1 is #{person1}"
puts "person2 is #{person2}"
produces:
person1 is Jim
person2 is Tim

nil

对象变量为空,nil
在条件判断时,被解读为 false

定义类

1
2
class YourClassName
end

例子:

1
2
3
4
5
6
7
class BookInStock
    def initialize(isbn, price)
        # 构造方法
        @isbn = isbn
        @price = Float(price)
    end
end

对象成员访问

默认情况下,对象成员不可读、不可写。

设置成员可读的方法,定义 accessor method

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class BookInStock
    def initialize(isbn, price)
        @isbn = isbn
        @price = Float(price)
    end

    def isbn
        @isbn
    end

    def price
        @price
    end

    # ..
end

book = BookInStock.new("isbn1", 12.34)
puts "ISBN = #{book.isbn}"
puts "Price = #{book.price}"

produces:
ISBN = isbn1
Price = 12.34

attr_reader

另外一种简便的开放成员可读的方法,使用 attr_reader 语句声明可读变量 :

1
2
3
4
5
6
7
8
9
10
class BookInStock
    attr_reader :isbn, :price

    def initialize(isbn, price)
        @isbn = isbn
        @price = Float(price)
    end

    # ..
end

setter方法

开放成员可写,就是定义一个方法名由“属性名”和“=”组成的方法

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
class BookInStock
    attr_reader :isbn, :price

    def initialize(isbn, price)
        @isbn = isbn
        @price = Float(price)
    end

    def price=(new_price)
        @price = new_price
    end

    # ...
end

book = BookInStock.new("isbn1", 33.80)
puts "ISBN = #{book.isbn}"
puts "Price = #{book.price}"
book.price = book.price * 0.75    # discount price
puts "New price = #{book.price}"

produces:
ISBN = isbn1
Price = 33.8
New price = 25.349999999999998

简便的方法,开放可读&可写,attr_accessor :

1
2
3
4
5
6
7
8
9
10
11
class BookInStock
    attr_reader    :isbn
    attr_accessor :price # price 成员可读、可写

    def initialize(isbn, price)
        @isbn = isbn
        @price = Float(price)
    end

    # ...
end

库引用

CSV 库: 解析CSV文件

1
require_relative 'csv_reader'

method的访问控制

直接访问控制申明的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyClass
    def method1 # default is 'public'
        #...
    end

protected        # subsequent methods will be 'protected'
    def method2  # will be 'protected'
        #... 
    end 

private          # subsequent methods will be 'private'
    def method3  # will be 'private'
        #...
    end

public           # subsequent methods will be 'public'
    def method4  # will be 'public'
        #...
    end

end

统一申明访问控制的例子:

1
2
3
4
5
6
7
8
9
10
11
class MyClass
    def method1
    end
    def method2
    end
    # ... and so on

    public    :method1, :method4
    protected :method2
    private   :method3
end

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Parent
    def say_hello
        puts "Hello from #{self}"
    end
end
p = Parent.new
p.say_hello

# Subclass the parent...
class Child < Parent
end
c = Child.new
c.say_hello

produces:
Hello from #<Parent:0x007fb87110fd98>
Hello from #<Child:0x007fb87110fac8>

查看对象

puts , p 皆可,例如:

1
2
3
4
5
6
7
8
9
10
class Person
    def initialize(name)
        @name = name
    end
end
p = Person.new("Michael")
puts p

produces:
#<Person:0x007fa08b8643f8>

Module

作为命名空间使用

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
# tut_modules/trig.rb

module Trig
    PI = 3.141592654
    def Trig.sin(x)
        # ..
    end
    def Trig.cos(x)
        # ..
    end
end

# tut_modules/moral.rb

module Moral
    VERY_BAD = 0
    BAD = 1
    def Moral.sin(badness)
        # ...
    end
end


# tut_modules/pin_head.rb

require_relative 'trig'
require_relative 'moral'
y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)

作为 Mixin 使用

However, you can include a module within a class definition.
When this happens, all the module’s instance methods are suddenly available as methods in the class as well. They get mixed in. In fact, mixed-in modules effectively behave as superclasses.

为多重继承提供了可能。著名的基础 Mixin-Module: Comparable , Enumerable

示例,自定义Module,并使用之:

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
module Debug
    def who_am_i?
        "#{self.class.name} (id: #{self.object_id}): #{self.name}"
    end
end

class Phonograph
    include Debug
    attr_reader :name
    def initialize(name)
        @name = name
    end
    # ...
end

class EightTrack
    include Debug
    attr_reader :name
    def initialize(name)
        @name = name
    end
    # ...
end

ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrealistic Pillow")

ph.who_am_i? # => "Phonograph (id: 70266478767560): West End Blues"
et.who_am_i? # => "EightTrack (id: 70266478767520): Surrealistic Pillow"

示例,使用系统的Enumerable Module,前提是实现 each 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# tut_modules/vowel_finder.rb

class VowelFinder
    # 找出所有元音字母
    include Enumerable
    def initialize(string)
        @string = string
    end
    def each
        @string.scan(/[aeiou]/) do |vowel|
            yield vowel
        end
    end
end

vf = VowelFinder.new("the quick brown fox jumped")
vf.inject(:+) # => "euiooue"

Class Method vs Instance Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo
  def self.bar
    puts 'class method'
  end

  def baz
    puts 'instance method'
  end
end

Foo.bar # => "class method"
Foo.baz # => NoMethodError: undefined method ‘baz’ for Foo:Class

Foo.new.baz # => instance method
Foo.new.bar # => NoMethodError: undefined method ‘bar’ for #<Foo:0x1e820>