###### tags: `Ruby & Rails` # 20221111 Rails 筆記 [TOC] --- <政庭> ## 物件導向程式設計- Object-Oriented Programming * OOP -> 金字塔結構 * 目的是做分類,以人類理解的方式呈現 * Ruby世界裡面,block、method(function)不是物件 * Ruby效能不是唯一考量。好寫、貼近人類語言趨向 ## Functional Programming 功能型程式設計 * FP ->(扁平的結構) ## 類別與實體 * 實體方法作用在實體身上 * 類別方法作用在類別身上 * 類別命名必須是"常數" ```ruby class Cat // 實體方法 def hi p "hi" end end kitty = Cat.new // 類別方法 kitty.hi ``` ## 繼承 ```ruby class Animal def eat end end class Cat < Animal end class Dog < Animal end kitty = Cat.new kitty.eat lucky = Dog.new lucky.eat ``` ## initialize(初始化) * initialize 是new完成後馬上接著做的第一件事 * 目的是要讓 new 出來的物件可以馬上有裝備起來 ```ruby class Cat # 建構、建構子 Constructor def initialize(nn) puts nn puts "hi" end def hey end end kitty = Cat.new("nancy") // 執行結果 nancy // hi ``` ```ruby # class Object # def initialize # end # end // 假設最上層有一個叫做Object的系統預設類別 class Animal end class Cat < Animal end kitty = Cat.new("nancy") // 結果顯示 錯誤訊息 引數個數錯誤 // 因為new完後馬上執行initialize // 往上找class Cat 沒有 initialize // 再往上找 class Animal 沒有initialize // 再往上找 看不到的 Object 裡面有一個沒有接收(參數)的initialize // 所以會看到 error訊息顯示 引數個數錯誤 ``` ## 實體變數 @變數名稱 * 實體變數只會作用實體上 * 每個實體變數,會有個別的實體變數,不會跟其他實體共享 ## Ruby 沒有屬性 ```ruby class Animal attr_accessor :age // 可讀可寫 attr_reader :age // 做 getter的事情 attr_writter :age // 做 setter的事情 def initialize(age) @age = age end # getter def age @age end # setter def age=(n) @age=n end end ``` ## 實體方法 VS 類別方法 * 實體方法作用在實體上 * 類別方法作用在類別上 ## 單體方法 Singleton method ```ruby class Cat def kitty.hi // 只幫kitty定義hi的方法 puts "hihihi" end end kitty = Cat.new nn = Cat.new kitty.hi // 執行結果 會印出 hihihi nn.hi // 執行結果 會出錯 ``` ## 類別方法 ```ruby class Cat # 類別方法 def self.fly // self = Cat 誰呼叫誰就是self puts "fly!!!" end def hi p "hi" end end Cat.fly kkk = Cat.new("cc") kkk.hi ``` ### Open Class ```ruby class Cat def hi end end class Cat def hwy end end // 這兩個方法都會存在,Ruby會把它"融合"起來 // Cat.instance_methods(false) ``` ## 類別變數 ```ruby class Cat @@count = 0 def initialize @@count += 1 end def self.total @@count end end ``` ## 手刻 attr_reader / attr_writter / attr_acessor ```ruby class Animal def self.my_attr_reder(attr) // define_method可以做到的事情,動態定義"方法"的 method。 define_mtthod(attr) do instance_variable_get(:"@#{attr}") end end def self.my_attr_writter(attr) define_mtthod("#{attr=}) do |val| instance_variable_set(:"@#{attr}",val) end end def my_attr_acessor(attr) self.my_attr_reder(attr) self.my_attr_writter(attr) end end class Cat < Amimal my_attr_reader :age end kitty = Cat.new p kitty.age ``` ## 封裝的三種方法 private VS public VS protected * 取用private方法時,請透過public方法來取用。 * 呼叫private方法時,不可以有.出現 // 早期版本Ruby不允許 * 呼叫private方法,前面不可有 self. * private方法,就算是在上層類別裡面被定義,下層類別都可以使用它 // private封裝 ```ruby class Cat def hi hey // 只要沒有"."就可以呼叫private方法 end private def hey p "private" end end kitty = Cat.new kitty.hey // 這行會出錯 kitty.send(:hey) // 這行會正確執行,表示它繞過了private的規則我沒有出現".",所以我可以呼叫pirvate方法。 ``` // protected封裝 ```ruby class Cat def hi self.hey // 可以有"."也可以不要有"." end protected def hey p "private" end end kitty = Cat.new kitty.hey // 可以正確執行 kitty.send(:hey) ``` ## 訊息與接收者 ```ruby kitty.say_hello() ``` #### 在Ruby世界繼承是單向的,且一次只能繼承一個單位 #### 此時有誕生了module這個好用的方法 * module就像海賊王的惡魔果實,吃下(include)果實可以獲得惡魔之力 * module 就是外掛在搜尋方法的路徑上。 * module 不能new / 不能繼承與被繼承 ```ruby module Flyable def fly puts "i can fly!!!" end end class Cat include Flyable // 把這個fly模組加裝到Cat類別 end kitty = Cat.new kitty.fly // 恭喜以後的貓都會飛了~~ ``` ```ruby class Animal end class Cat < Animal end p Cat.superclass // 印出 Animal kitty = Cat.new p kitty.class // 印出 Cat p Cat.superclass.superclass // 印出 Object p Cat.superclass.superclass.superclass // 印出 BasicObject p Cat.superclass.superclass.superclass.superclass // 印出 nil * 故可得知最最上層就是BasicObject * ``` ### Ruby 找方法源頭的流程很像 javascrpit 的__proto__的關係鏈 ```ruby p Cat.ancestors // 找出Cat繼承哪些祖先們 // [Animal Oject Kernel BasicObject] ``` ### 繼承體系 與 Kernel模組 ```ruby module Flyable def fly puts "i can fly!!!" end end class Cat < Animal include Flyable // 把這個fly模組加裝到Cat類別 end p kitty.ancestors // 會發現多出fly模組[Cat Flyable Animal Oject Kernel BasicObject] ``` ## Module 是類別,所以它可以 new 物件出來 * Module = Class.new // Module 是 Class 這個類別分類出來的"類別" * ff = Module.new // ff 是Module new 出來的 "模組" ## class VS superclass * 了解類別之間的繼承關係 ## extend(擴充) * 跟includes 的差別就是 extend 是幫類別擴充module ```ruby module Flyable def fly puts "i can fly!!!" end end class Cat extend Flyable // 這邊 Cat 這個類別就擴充飛行模組 end Cat.fly ``` ## namespace(命名空間) ## 為什麼view可以使用Controller裡面的實體變數? * 因為在Controller 裡面 正在執行的程式碼還沒做完 * erb檔案 ---> 會去找正在執行中的程式碼有宣告一個實體變數,erb有需要使用它,就會把它拿去用。 --- <侑庭> ## 物件導向程式設計Object-Oriented Programming 1. 物件導向最終目的就是在做分類,並用人類理解的方式呈現 2. Ruby 唯二不是物件就是 method , block 3. 如何去分辨不是物件 就是辦法在物件之後加東西 a 是物件在 ```ruby= def tim end a.tim ``` 4. 類別一定是要常數,常數一定是大寫開頭 ## 繼承 1. 像金字塔把方法存在金字塔頂端, 之後在這金字塔底下的都會共同有這功能 ```ruby= class Animal #頂端 def eat end end class Cat < Animal #向下延伸都會繼承方法 end class Dog < Animal end kitty = Cat.new kitty.eat lucky = Dog.new lucky.eat ``` ## 初始化 initialize 1. 在new一個新的實體的時候就會直接呼叫initializa的方法,在看不見得地方一定會有一個init 2. 在JS中是使用constructor 3. ```ruby= class Word #只是沒標示,出來其實word後面還是會有接ruby內建的class 讓 word 繼承只是不會特別寫出來 ,所以只要new就一定會呼叫initialize 就算原本word裡面得class沒寫他也會一直網上找到有initialize設定的 def initializa #在呼叫new 之後就 會直接呼叫initializa puts "hi" end end tim = Word.new ``` ## 類別方法 ```ruby= #單體方法 Singletion method #只有幫kitty加 method def kitty.hi puts "hihihi" end kitty.hi #印出hihihi nn.hi #印出 defind method 'hi' ``` ## 類別加self * 重點誰呼叫誰就是self ,要依照成級來看self是實體還是類別,就像JS的this ## 當遇到兩個名字一樣的class * 兩個會融合再一起不會壞掉,而是兩個都可以取用不會重複 <下午> ## 存取控制 * 要存取private 必須要按按鈕, 就需要把他寫在public裡面就可以拿到了 * private 就很像專利,有寫東西不想讓別人知道就會把他寫在private下面 * 取用private 前面不能有小數點,不管在public方法內 或是在外面 * 但在版本3.1.2 中 在public放法中`self.private` 是可以取得 private 的,但是當他不存在 * 如果要在外部取用 `kitty.send(:private)` 可以取的private裡面的東西 * 繼承的部分沒有所為被 private 綁在上層的問題,只要是繼承就是所有方法都可以用,但就是還是要符合private 前面不能有小數點的設定 ## 模組 * 模組不像繼承,模組是需要什麼就去引入什麼,而繼承是上面有什麼我就全部都拿到不管好壞,這就是兩個的差異性 ```ruby= module Flyable def fly puts "I can fly" end end class Cat include Flyable #用include 引入模組 end kitty = Cat.new kitty.fly ``` * 用Cat.superclass 可以找到class上層 * 用Cat.ancestors 可以找到所有祖先 * 模組會在class跟class中間原本沒東西,但在某個class 可能有被 include()模組進來了 , 就會在被夾在中間 * 模組依照後進來先出 * class 比 module 多了四個方法, 多了繼承 ,跟new的功能 * superclass是分類他們都是class 去做出來 只是用模組類別new出來的模組不能繼承及new * extand 可以擴充作用範圍把原本只做用到實體上,擴充到類別也可以用 ## namespace * R::Cat < namespace , name: < key , :name < symble ```ruby= module a module b class Cat end end end kitty = a::b::Cat.new #a 類別/模組裡面有, b 類別/模組裡面有, C 類別 ``` --- <于婷> ## Ruby 唯二不是物件? method , block ## 類別繼承使用時機 因為類別多了繼承的能力, 如果彼此間是有關連的, 甚至是要有另一類別內的所有能力, 可以考慮使用類別繼承。 例如我們產生一個動物類別會動、能吃東西, 然後我們要產生一樣會動、能吃東西的魚類別、鳥類別、狗類別, 我們就可以讓這三個類別都繼承動物類別, 這樣這三個類別所有實體都會動、能吃東西。 ## initialize(初始化) * 只要一出生就會執行(new實體時一並執行) * initialize 多練習打~有四個i,打錯就沒有了 * 初始化行為又稱建構或建構子 * JS也有類似行為:constructor ## superclass(超類別/父類別) ```ruby clss Animal end p Animal.superclass #Object p Class.superclass #Module p Module.superclass #Object p Object.superclass #BasicObject p BasicObject.superclass #nil ``` ## 實體與實體變數 實體是一種實體(位置)、也是一種變數。 實體變數是一種實體變數、也是一種變數,只活在實體方法內。 ## RB沒有屬性只有方法,可以使用以下方式 ```ruby attr_accessor :age #可讀可寫 attr_reader :age #可讀 attr_writer :age #可寫 ``` ## 單體(例)方法 => 類別方法 v.s 實體方法 ```ruby def kitty.hi #單體(例)方法 p "hi" end class Cat def self.hi #類別方法 p "hi" end def haha #實體方法 p "haha" end end Cat.hi #類別方法 kitty = Cat.new #類別方法 kitty.hi #單體(例)方法 kitty.haha #實體方法 ``` 要使用實體方法時,new出實體再使用實體方法 ## 類別變數使用方式 ```ruby class Cat @@count = 0 def initialize @@count += 1 end def self.total @@count end end ``` ## public(公用的) 系統預設定義是public, 是一種開放的、誰都可以存取的方式。 ```ruby class Cat def eat puts "eating..." end end kitty = Cat.new kitty.eat #eating... ``` 像這樣,kitty可以直接提取eat這個方法。 ## Protected(受保護的) 較public不自由, 不可以直接存取, ```ruby class Cat protected def eat puts "eating..." end end kitty = Cat.new kitty.eat #錯誤訊息 ``` 但不限定有沒有明確的接收者, 所以用以下兩個方法都可以成功執行。 ```ruby class Cat def do_eat eat #沒有接收者 self.eat #有接收者 end protected def eat puts "eating..." end end kitty = Cat.new kitty.do_eat ``` ## Private(私人的) 相對於public, 不可以直接存取, ```ruby class Cat private def eat puts "eating..." end end kitty = Cat.new kitty.eat #錯誤訊息 ``` 也不能有明確的接收者, 因此在呼叫方法時不能有.小數點 ```ruby class Cat def do_eat eat #沒有接收者,但是在ruby3版本後可寫成self.eat end private def eat puts "eating..." end end kitty = Cat.new kitty.do_eat ``` 不過在ruby的世界裡, 還可以用send方法來呼叫成功喔! ```ruby class Cat private def eat puts "eating..." end end kitty = Cat.new kitty.send(:eat) #動態執行 ``` ## 所有的.class都是Class ## namespace ``` kitty = A::B::Cat.new ``` 代表 ``` module(或class) A module(或class) B class Cat end end end ``` ---