---
# System prepended metadata

title: Ruby(03/14) Day 29 五倍紅寶石(第八屆共筆)(2021/8/05)
tags: [Ruby]

---

# Ruby(03/14) Day 29 五倍紅寶石(第八屆共筆)(2021/8/05)
## Object-Oriented Programming(OOP) 物件導向程式設計
使用人類比較容易理解的概念，
主要核心概念是傳遞訊息，物件與物件之間的**連結**才是重點，不要被名字給影響。
物件好用的地方在於能夠使用方法去跟別的物件互動，才有辦法幫我們做事。
物件 ＝ 狀態(名詞) ＋ 行為(動詞)。
在 Ruby 裡幾乎所有東西都是物件。
只有 **block 跟 method** 不是物件。

### 方法補充
Ruby 不像 JS 有高階函式，不能用方法回傳方法。
定義方法時，Ruby 會將方法的名稱宣告成符號。

有興趣了解更多OOP的概念可以看下面這部影片，是之前龍哥貼的影片其中一位講得很好的講者講的。
[Object Oriented Programming is not what I thought - Talk by Anjana Vakil](https://www.youtube.com/watch?v=TbP2B1ijWr8)

## 類別&實體
### 類別
Ruby 的類別也是物件，別的程式不一定。
- 定義:
定義類別要用大寫英文開頭，也就是用**常數**定義。

- 用處:
  - 用來分類，把同樣特徵的都歸在同一類
  - 類別可以產生實體，把方法定義在裡面就可以讓他生出來的實體去用

#### 類別方法 ＝ **作用在類別上的方法 = 他被類別呼叫，所以他就是類別方法**。
類別方法會被定義在那個類別裡面。
all 接在 Cat 後面，Cat 是類別，所以 all 是類別方法。
self = JS 的 this，指這個類別本身，方便使用。
誰呼叫誰就是 self，這邊的 self 就是 Cat 本身。
以後如果要修改 Cat 名稱也沒差，不用改函式裡面的名字。
```=rb
class Cat
  def self.all
    puts "全部的貓兒"
  end
end

Cat.all
```
- 如何定義:
```=rb
# 方法1:
class Cat
  def self.all
    puts "全部的貓兒"
  end
end
Cat.all

# 方法2:
class Cat
  class << self
    def all
      puts "全部的貓兒"
    end
end
Cat.all

# 方法3:
class Cat
end
def Cat.all
  puts "全部的貓兒"
end
Cat.all
```
### 實體
#### 實體方法 ＝ **作用在實體上的方法 = 他被實體呼叫，所以他就是實體方法**。
實體方法會被定義在那個實體的類別裡面。
say_hello 接在 kitty 後面，kitty 是 Cat 類別生出來的實體，所以 say_hello 是實體方法。
```=rb
class Cat
  def say_hello
    puts "你好"
  end
end

kitty = Cat.new
kitty.say_hello
```
實體方法是共用的，讓生出來的實體都有共同行為。
ex:都會走路

- 如何定義:
```=rb
# 方法1:
class Cat
  def all
    puts "全部的貓兒"
  end
end
Cat.new.all

# 方法2:
class Cat
  attr_accessor :all
end
kitty = Cat.new
kitty.all = "全部的貓兒"
p kitty.all
```


### initialize 初始化
透過 new 可以建立實體，
被 new 出來的物件會立即做 initialize 裡面的方法，還沒呼叫他就會執行了。
可以帶引數給他，變成他的參數，但多給或少給都會出錯 (ArgumentError)。
預設 initialize 裡面是空的。

- why?
一出生就會做事，生出來有預設值，比較方便做事。
#### 類別變數 @@開頭的
在類別方法裡可取用的變數。
```rb
class Cat
  @@count = 0
  def initialize
    @@count += 1
  end
  def self.counter
    return @@count
  end
end

5.times {
  Cat.new
}
# 印出 5
p Cat.counter
```

#### 實體變數 @開頭的
在實體裡面可以自由取用的變數，定義在類別裡，不能直接在外面拿到，要透過方法回傳到外面。
實體變數只會存活在個別的實體上(傳進去的'kitty'只會活在 kitty 實體上)，資料可以透過 new 後面的引數傳進去。
ex: 個別資料:名字、住哪、電話幾號等等的。
如果直接在外面使用 name 方法會發生 NoMethodError，因為 Cat 裡面根本沒有 name 這個方法可以用。
```=rb
class Cat
  def initialize(name)
    @name = name
  end
  def say_my_name
    return @name
  end
end

kitty = Cat.new('kitty')
puts kitty.say_my_name
```
#### new 跟 initialize 的關係：
new 是用來生實體出來的，後面帶進去的引數會傳給 initialize 當他的參數用。
參數又會指派給實體變數，接著用方法讓他傳到外面才能被呼叫出來。

#### 小結：
- 實體變數@
  在實體方法裡可以自由取用的變數。
  只能活在類別裡面，想拿出去要透過實體方法傳出去，用在呼叫實體方法時要把值帶出來的時候。
  方法是共用的，實體變數只會存活在個別的實體上。
- 類別變數@@(少用)
  在類別方法裡可取用的變數。
  只能活在類別裡面，想拿出去要透過類別方法傳出去，用在呼叫類別方法時要把值帶出來的時候。
- 實體方法
  作用在實體上的方法 = 他被實體呼叫，所以他就是實體方法
- 類別方法
  作用在類別上的方法 = 他被類別呼叫，所以他就是類別方法

---

## singleton method 單點方法
可以再任意物件上定義任意方法 ＝ 只會對那顆物件有效的方法，不過通常不會這樣用。
因為類別也是物件，所以可以在 Dog 類別上建立方法。
```rb
class Dog
  def self.fly ＝ Dog.fly ＝ singleton method
  end
end

Dog.fly
```
### main
外面的 self = main
main 是 Object 類別所建立的實體，也就是說 main = Object.new。
誰呼叫誰就是 self，self 沒人呼叫就是代表外面的大泡泡 = 整個 Ruby 執行環境。
```rb
p self => 印出 main
```

## 如何拿出、取用實體、類別變數
### 正常拿取:使用 getter & setter
- getter 用來把變數拿出去
- setter 用來把變數射進來
  setter 後面加 = 是慣例、語法糖衣，為了讓他看起來像是在設定屬性的值給他，但其實 Ruby 根本沒有屬性只有方法，=只是name的一部分而已，這個方法的名字就叫做name=。
```=rb
class Human
  def initialize(name)
    @name = name
  end
  # getter 拿出：把拿小孩出去
  def name
    @name
  end
  # setter 接收：射進來
  def name=(new_name)
    @name = new_name
  end
end

eddie = Human.new("eddie")
# 印出 "eddie"
p eddie.name
eddie.name = "Ruby"
# 印出 "Ruby"
p eddie.name
```

### 懶人拿取法:使用 attr_reader、attr_writer、attr_accessor
是屬性快捷鍵 ＝ 類別方法。
- attr_reader = 可讀 = getter
- attr_writer = 可寫 = setter
- attr_accessor = 可讀可寫 = 融合 getter + setter

- 用法:可用符號或字串來取用，本身是方法，()內是參數
  attr_accessor（:name, :age）
  attr_accessor :name, :age  => 通常會省略()
  
為什麼有可讀可寫還要有可讀跟可寫功能?
=> 權限控管，有的事情不要開放讓使用者用。
ex:讓使用者自己修改帳戶的錢會很可怕。

## open class 開放類別
同名 class 可重複定義 class ，裡面的方法如果名字不同不會被蓋掉，會融合在一起。
但如果裡面的方法也同名那後面的方法會蓋掉前面的。
應用:可以幫所有物件(包括原生類別)定義(增加)方法。

```rb
class A
  def aa
end
class A
  def aa
  def bb
end
```
- **常數可以被修改是為了讓 open class 能正常運作**
以上面例子為例:
1. 建立 class A，裡面有方法 aa
2. Ruby 看到又建立一個名字一樣的 class A 會建立一個新的物件
3. 把前面的 class A 指向新物件(這時候常數被 reassigned)
4. 把後面的 class A 裡面的方法加進去新物件
5. 發現有同名的方法，後面的 def aa 會蓋掉前面的

## Object Model
- a.class 可以看到 a 物件是誰生出來的，找他的 class。
- a.superclass 可以找到上層的 class。
- 所有的class的class都是class，包括他自己
可用 .class 搭配 .superclass 找到最上層的人是誰。
類似 JS 的找原型，JS 是往末端找(沒有繼承的概念)，Ruby 是往上層找(有繼承的概念)。
當使用方法的時候，如果 Ruby 在他的 class 找不到這個方法就會一直往上層找，直到找到為止。
例子:
```rb
class Animal
end

class Cat < Animal
  def initialize(name)
    @name = name
  end
  def say_my_name
    return @name
  end
end

kitty = Cat.new('kitty')

kitty.class = Cat
Cat.superclass = animal
.
.
.
kitty => animal => Object => BasicObject => nil => NoMethodError
```
class 的上層是 module 再往上會回到 Object
```rb
class.superclass => module => Object
```

## Rake
是 Ruby 實做的伺服器的介面，不是伺服器，是網頁跟伺服器中間的橋樑。
ROR是一種Rack的大型應用程式。

request：我輸入的內容
response：輸出結果

- Rake 是一種規格，會提供一個能夠回應 call 方法(call 方法是用來執行物件的方法)的物件(使用 Proc 或 lambda 讓 block 變成物件，才可以用 call 方法)給網頁，並回傳一個包含以下三個元素的陣列：
  - 狀態(數字)
  - header(Hash)
  - body(陣列)

- HTTP 狀態碼:
  - 1開頭:參考資訊
  - 2開頭:成功
  - 3開頭:重新導向(轉址)
  - 4開頭:使用者的錯
    404：我這邊沒東西，你硬要來我這邊找。
    header、body正確還是可以看得到網站內容，用404表示不存在，跟 nil 一樣。
  - 5開頭:伺服器的錯
    伺服器找不到這個頁面

指令:
- rackup
  執行
- control c 
  停止它
- PATH_INFO
  找哪個網址(路徑)

## Rails
是Ruby的加強版，用開放類別幫現有類別做了很多擴充，多了很多好用的方法。

---

###### tags: `Ruby`
