[筆記]Ruby語言入門-物件

| Comments

上回Ruby語言入門-方法與區塊之後

這回要來筆記Ruby的物件

在Ruby裡面,可以說幾乎所有東西都是物件

每個物件都有他的狀態跟行為

以下面這個前一回就有介紹過的ruby程式碼

1
   5.times

這邊看到,"5"這個數字本身就是個物件

然後它有一個方法叫做times

以之前學到物件導向程式語言的概念

我會認為是5這個物件上呼叫了times這個方法

但實際上在Ruby他是用一個接收者傳遞訊息的概念

5是接收者(recever)

times是訊息(message)

意義是我們對這個物件傳送一個訊息,並不是5這個物件上呼叫了times這個方法

這概念來自smalltalk這個古老的語言,有類似概念的語言還有objective c

類別(Class)

類別在Ruby的樹狀結構圖裡本身也是個物件

他的用意是在做物件的“分類”

就像我們的世界也分類成界,門,綱,目,科,屬,種

試著在ruby裡定義動物的類別

1
2
3
4
5
  #類別的命名有個規定,就是必須是常數

  class Animal

  end

上面很簡單的就定義了一個動物的類別

通常動物都會有睡覺的行為

所以我們可以這樣幫動物定義睡覺的行為

1
2
3
4
5
  class Animal
     def sleep
      puts "zzz"
     end
   end

由於類別本身是抽象的,沒有一個實體

類別就像建築藍圖一樣,我們必須去依照這個藍圖去蓋一個建築物出來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  #定義好類別
  class Animal
     def sleep
      puts "zzz"
     end
   end

  #實作類別實體
  #比如我們可以說“人”是動物的一種

  person = Animal.new #實作一個叫做人的動物

  #因為動物裡面有定義了睡覺這個行為
  #以動物這個類別藍圖所建構出來的人理所當然也會有睡覺的行為

  person.sleep() #輸出  zzz

我們可以說上面的person是由Animal建構出來的類別實體

類別的繼承

雖然我們可以說人類是個動物

但不代表動物一定就是人類

所以我們可以自己去幫原本的動物類別做擴充

以動物這個類別再去細分更多的類別

例如我們可以再去細分一個“人類”的類別

並且新增人類會有的行為

我們可以這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  class Animal
     def sleep
      puts "zzz"
     end
   end

  #定義人類的類別並且繼承擴充自動物
  class Human < Animal
     def talk
         puts "你好嗎?"
     end
  end


  person = Human.new #實作一個人
  person.talk() #這個人會講話
  #由於人是由Animal擴充來的,自然也會睡覺了
  person.sleep() #輸出  zzz

這樣我們就能製造很多人了XD

實體變數

前面有說過一個物件是由狀態跟行為構成的

比如說一個人,他現在的狀態是18歲的青少年,名字叫sayaku,他的行為有走路,跑步,講話等等

在物件導向的程式語言裡,我們會稱這些狀態為物件的屬性,行為會稱為物件的方法

寫起來大概就像這樣

1
2
3
4
5
6
7
8
 #物件的屬性 
 person.age=18
 person.name="sayaku"

 #物件的方法
 person.sleep()
 person.run()
 ......

現在我們要來定義這個青少年的狀態

希望在建構這個人的時候順便定義他的歲數

1
2
3
4
5
6
7
8
9
10

  class Human < Animal
      #實體建構時帶參數,預設會先去帶到initialize這個方法

      def initialize(age)
           @age = age
      end
  end

  person = Human.new(18) #建構的時候帶18的參數

上方的程式,我們看到在建構人類的實體時,順便告訴類別我的年齡是18歲

並且指定到@age這個實體變數

所以在ruby的類別裡,多了一個前綴符號@就代表實體變數

也就是說理論上我們使用下面這個方式應該能夠預期這個人的歲數是18

1
     puts person.age

但不幸的,這樣子Ruby會跟你說person這個實體沒有age這個“方法”

那該怎麼辦?

那就幫這個物件增加一個age的方法吧

1
2
3
4
5
6
7
8
9
10
11
12
13

  class Human < Animal
      def initialize(age)
           @age = age
      end

      def age
         return @age
      end
  end

  person = Human.new(18) #建構的時候帶18的參數
  puts person.age   #輸出 18,

上面的person.age原本是person.age()

但因為在ruby裡括號是能省略,所以這邊看起來就像是屬性一樣

那因為年齡會隨著時間流逝而改變

所以我們可以期待改變年齡應該要這樣寫

1
2
3
     person.age= 19

     puts person.age

很不幸的,ruby又告訴你沒有age=這個方法

這邊就蠻有趣的,因為之前有說過,Ruby的方法命名是可以帶符號的

所以這邊就生一個age=的方法出來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  class Human < Animal
      def initialize(age)
           @age = age
      end

      def age
         return @age
      end

      def age= (age)
         @age=age
      end
  end

  person = Human.new(18)
  puts person.age   #輸出 18

  person.age=19
  puts person.age   #輸出 19

這樣就可以了!

所以實際上person.age=19這一段,原本應該是長這樣person.age= (19)

從上面的實作過程中可以了解到,ruby其實是沒有實體的屬性,全部都是透過方法來達成

在程式裡利用方法來存取屬性,我們可以稱為類別的封裝

也是就是其他程式語言常看到的getter and setter

另外我們也可以發現,要設定或是提取實體變數的值時都要個別的幫他定義方法,這實在太麻煩了

所以裡面的ageage=這兩個方法可以再簡寫成

1
2
3
4
5
6
7
8
9
  class Human < Animal

      attr_reader :age
      attr_writer :age

      def initialize(age)
           @age = age
      end
  end

這樣的效果也是一樣

當然多加了這兩行又覺得太麻煩

所以又可以再簡寫成一行

1
2
3
4
5
6
7
8
class Human < Animal

      attr_accessor  :age

      def initialize(age)
           @age = age
      end
end

類別方法

前面有說過,類別本身也是物件

既然是物件,理當可以有自己的行為方法

就好比類別是藍圖的紙,實體是依照藍圖蓋出來的建築物

例如藍圖的紙可以被撕掉或是供人畫圖

這些可以當作藍圖這張紙的方法

那類別方法該怎麼定義?

在定義之前我們可以看看Ruby的一個特性

就是Ruby可以在任意物件上加入任意方法

如下面

1
2
3
4
5
6
7
   #定義貓的類別

   class Cat
   end

   #實作出一隻貓
   kitty=Cat.new

如果今天我們想讓這隻貓有會叫的方法

我們除了在Cat類別裡面定義外還有其他的方法嗎?

我們可以這樣做

1
2
3
4
5
6
7
8
9
10
11
12
13
   #定義貓的類別

   class Cat
   end

   #實作出一隻貓
   kitty=Cat.new

   def kitty.say_hello
  puts "hi"
   end

   kitty.say_hello  #輸出hi

上面可以看到,我們可以在任意物件上加入任意方法

所以這時這隻貓就有say hello的行為了

當然這樣寫的,也只有這隻kitty貓有這種行為

其他由Cat實作出來的貓就不會有這方法

這在其他的程式語言又叫單體方法(Singleton method)

Ruby可以很簡單的達成這種需求

由於剛剛說的Ruby可以在任意物件上加入任意方法

類別本身又是物件

所以可以寫成這樣

1
2
3
4
5
6
7
8
9
  class Cat

  end

  def Cat.all
  puts "我是類別的方法"
  end

  Cat.all  #輸出 我是類別的方法

所以實際上類別方法是作用在類別上的單體方法

可以再簡化到類別自己的裡面

1
2
3
4
5
6
7
8
  class Cat
      def self.all
         puts "我是類別的方法"
      end  
  end


  Cat.all  #輸出 我是類別的方法

這邊的self會指向到類別本身

如果我同時要定義好幾個類別方法,每個方法前都要寫self還蠻麻煩的

所以你可以這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  class Cat
      class << self   
        #放在lass << self end裡面就不用再加self
          def all
             puts "我是類別的方法"
          end
      end
      .
      .
      .
      .
  end


  Cat.all  #輸出 我是類別的方法

開放類別(Open Class)

在ruby裡,如果同名的兩個類別,ruby會自動幫你做合併

如果類別內有同名的方法,那後面定義的方法會複寫掉前面所定義的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  class AA
     def hi
         puts "hi"
     end
  end

  class AA
     def hi
         puts "hihi"
     end

     def okok
         puts "okok"
     end

   end


   #以上ruby會把兩個AA合併起來,重複定義的方法會以後面定義的為主
   a=AA.new
   a.hi      #輸出 hihi
   a.okok     #輸出 okok

重複定義的方法會以後面定義的為主,但如果我想用被覆蓋前的方法定義可以用

#alias_method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    class BB
      def hi
            puts "first"
      end
    end

    class BB
      alias_method :old_hi, :hi #將上一個hi的方法對應到old_hi
          def hi
                  puts "last"
          end
      end  

     b=BB.new
     b.hi          #輸出 last
     b.old_hi      #輸出 first

可以用在如果原作者原方法寫很爛,但是又不得不用到他可以用這個方法,保留舊方法,用自己新的方法

可以隨時把類別拿出來修改,這就叫開放類別

但因為很容易複寫原本方法的定義,即使是內建的類別要拿出來惡搞複寫也可以

所以這種特性是把雙面刃,有些人也不喜歡這樣,就稱為猴子補丁

Comments