<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 4.4 Ruby 類 我們之前說過,Ruby 中的一切都是對象。本節我們要自己定義一些對象。Ruby 和其他面向對象的語言一樣,使用類來組織方法,然后實例化類,創建對象。如果你剛接觸“面向對象編程”(Object-Oriented Programming,簡稱 OOP),這些聽起來都似天書一般,那我們來看一些實例吧。 ## 4.4.1 構造方法 我們看過很多使用類初始化對象的例子,不過還沒自己動手做過。例如,我們使用雙引號初始化一個字符串,雙引號是字符串的字面構造方法: ``` >> s = "foobar" # 使用雙引號字面構造方法 => "foobar" >> s.class => String ``` 我們看到,字符串可以響應 `class` 方法,返回值是字符串所屬的類。 除了使用字面構造方法之外,我們還可以使用等價的“具名構造方法”(named constructor),即在類名上調用 `new` 方法:[[15](#fn-15)] ``` >> s = String.new("foobar") # 字符串的具名構造方法 => "foobar" >> s.class => String >> s == "foobar" => true ``` 這段代碼中使用的具名構造方法和字面構造方法是等價的,只是更能表現我們的意圖。 數組和字符串類似: ``` >> a = Array.new([1, 3, 2]) => [1, 3, 2] ``` 不過哈希就有點不同了。數組的構造方法 `Array.new` 可接受一個可選的參數指明數組的初始值,`Hash.new` 可接受一個參數指明元素的默認值,就是當鍵不存在時返回的值: ``` >> h = Hash.new => {} >> h[:foo] # 試圖獲取不存在的鍵 :foo 對應的值 => nil >> h = Hash.new(0) # 讓不存在的鍵返回 0 而不是 nil => {} >> h[:foo] => 0 ``` 在類上調用的方法,如本例的 `new`,叫“類方法”(class method)。在類上調用 `new` 方法,得到的結果是這個類的一個對象,也叫做這個類的“實例”(instance)。在實例上調用的方法,例如 `length`,叫“實例方法”(instance method)。 ## 4.4.2 類的繼承 學習類時,理清類的繼承關系會很有用,我們可以使用 `superclass` 方法: ``` >> s = String.new("foobar") => "foobar" >> s.class # 查找 s 所屬的類 => String >> s.class.superclass # 查找 String 的父類 => Object >> s.class.superclass.superclass # Ruby 1.9 使用 BasicObject 作為基類 => BasicObject >> s.class.superclass.superclass.superclass => nil ``` 這個繼承關系如[圖 4.1](#fig-string-inheritance-ruby-1-9) 所示。可以看到,`String` 的父類是 `Object`,`Object` 的父類是 `BasicObject`,但是 `BasicObject` 就沒有父類了。這樣的關系對每個 Ruby 對象都適用:只要在類的繼承關系上往上多走幾層,就會發現 Ruby 中的每個類最終都繼承自 `BasicObject`,而它本身沒有父類。這就是“Ruby 中一切皆對象”技術層面上的意義。 ![string inheritance ruby 1 9](https://box.kancloud.cn/2016-05-11_5732bcff57ec5.png)圖 4.1:`String` 類的繼承關系 要想更深入地理解類,最好的方法是自己動手編寫一個類。我們來定義一個名為 `Word` 的類,其中有一個名為 `palindrome?` 方法,如果單詞順讀和反讀都一樣就返回 `true`: ``` >> class Word >> def palindrome?(string) >> string == string.reverse >> end >> end => :palindrome? ``` 我們可以按照下面的方式使用這個類: ``` >> w = Word.new # 創建一個 Word 對象 => #<Word:0x22d0b20> >> w.palindrome?("foobar") => false >> w.palindrome?("level") => true ``` 如果你覺得這個例子有點大題小做,很好,我的目的達到了。定義一個新類,可是只創建一個接受一個字符串作為參數的方法,這么做很古怪。既然單詞是字符串,讓 `Word` 繼承 `String` 不就行了,如[代碼清單 4.12](#listing-word-class) 所示。(你要退出控制臺,然后再在控制臺中輸入這寫代碼,這樣才能把之前的 `Word` 定義清除掉。) ##### 代碼清單 4.12:在控制臺中定義 `Word` 類 ``` >> class Word < String # Word 繼承自 String >> # 如果字符串和反轉后相等就返回 true >> def palindrome? >> self == self.reverse # self 代表這個字符串本身 >> end >> end => nil ``` 其中,`Word &lt; String` 在 Ruby 中表示繼承([3.2 節](chapter3.html#static-pages)簡介過),這樣除了定義 `palindrome?` 方法之外,`Word` 還擁有所有字符串擁有的方法: ``` >> s = Word.new("level") # 創建一個 Word 實例,初始值為 "level" => "level" >> s.palindrome? # Word 實例可以響應 palindrome? 方法 => true >> s.length # Word 實例還繼承了普通字符串的所有方法 => 5 ``` `Word` 繼承自 `String`,我們可以在控制臺中查看類的繼承關系: ``` >> s.class => Word >> s.class.superclass => String >> s.class.superclass.superclass => Object ``` 這個繼承關系如[圖 4.2](#fig-word-inheritance-ruby-1-9) 所示。 ![word inheritance ruby 1 9](https://box.kancloud.cn/2016-05-11_5732bcff689e6.png)圖 4.2:[代碼清單 4.12](#listing-word-class) 中定義的 `Word` 類(非內置類)的繼承關系 注意,在[代碼清單 4.12](#listing-word-class) 中檢查單詞和單詞的反轉是否相同時,要在 `Word` 類中引用這個單詞。在 Ruby 中使用 `self` 關鍵字[[16](#fn-16)]引用:在 `Word` 類中,`self` 代表的就是對象本身。所以我們可以使用 ``` self == self.reverse ``` 來檢查單詞是否為“回文”。其實,在類中調用方法或訪問屬性時可以不用 `self.`(賦值時不行),所以也可以寫成 `self == reverse`。 ## 4.4.3 修改內置的類 雖然繼承是個很強大的功能,不過在判斷回文這個例子中,如果能把 `palindrome?` 加入 `String` 類就更好了,這樣(除了其他方法外)我們可以在字符串字面量上調用 `palindrome?` 方法。現在我們還不能直接調用: ``` >> "level".palindrome? NoMethodError: undefined method `palindrome?' for "level":String ``` 有點令人驚訝的是,Ruby 允許你這么做,Ruby 中的類可以被打開進行修改,允許像我們這樣的普通人添加一些方法: ``` >> class String >> # 如果字符串和反轉后相等就返回 true >> def palindrome? >> self == self.reverse >> end >> end => nil >> "deified".palindrome? => true ``` (我不知道哪一個更牛:Ruby 允許向內置的類中添加方法,或 `"deified"` 是個回文。) 修改內置的類是個很強大的功能,不過功能強大意味著責任也大,如果沒有很好的理由,向內置的類中添加方法是不好的習慣。Rails 自然有很好的理由。例如,在 Web 應用中我們經常要避免變量的值是空白(blank)的,像用戶名之類的就不應該是空格或[空白](http://en.wikipedia.org/wiki/Whitespace_(computer_science)),所以 Rails 為 Ruby 添加了一個 `blank?` 方法。Rails 控制臺會自動加載 Rails 添加的功能,下面看幾個例子(在 `irb` 中不可以): ``` >> "".blank? => true >> " ".empty? => false >> " ".blank? => true >> nil.blank? => true ``` 可以看出,一個包含空格的字符串不是空的(empty),卻是空白的(blank)。還要注意,`nil` 也是空白的。因為 `nil` 不是字符串,所以上面的代碼說明了 Rails 其實是把 `blank?` 添加到 `String` 的基類 `Object` 中的。[8.4 節](chapter8.html#remember-me)會再介紹一些 Rails 擴展 Ruby 類的例子。) ## 4.4.4 控制器類 討論類和繼承時你可能覺得似曾相識,不錯,我們之前見過,在靜態頁面控制器中([代碼清單 3.18](chapter3.html#listing-adding-the-about-page)): ``` class StaticPagesController < ApplicationController def home end def help end def about end end ``` 你現在可以理解,至少有點能理解,這些代碼的意思了:`StaticPagesController` 是一個類,繼承自 `ApplicationController`,其中有三個方法,分別是 `home`、`help` 和 `about`。因為 Rails 控制臺會加載本地的 Rails 環境,所以我們可以在控制臺中創建一個控制器,查看一下它的繼承關系:[[17](#fn-17)] ``` >> controller = StaticPagesController.new => #<StaticPagesController:0x22855d0> >> controller.class => StaticPagesController >> controller.class.superclass => ApplicationController >> controller.class.superclass.superclass => ActionController::Base >> controller.class.superclass.superclass.superclass => ActionController::Metal >> controller.class.superclass.superclass.superclass.superclass => AbstractController::Base >> controller.class.superclass.superclass.superclass.superclass.superclass => Object ``` 這個繼承關系如[圖 4.3](#fig-static-pages-controller-inheritance) 所示。 我們還可以在控制臺中調用控制器的動作,動作其實就是方法: ``` >> controller.home => nil ``` `home` 動作的返回值為 `nil`,因為它是空的。 注意,動作沒有返回值,或至少沒返回真正需要的值。如我們在[第 3 章](chapter3.html#mostly-static-pages)看到的,`home` 動作的目的是渲染網頁,而不是返回一個值。但是,我記得沒在任何地方調用過 `StaticPagesController.new`,到底怎么回事呢? 原因在于,Rails 是用 Ruby 編寫的,但 Rails 不是 Ruby。有些 Rails 類就像普通的 Ruby 類一樣,不過也有些則得益于 Rails 的強大功能。Rails 是單獨的一門學問,應該和 Ruby 分開學習和理解。 ![static pages controller inheritance](https://box.kancloud.cn/2016-05-11_5732bcff7cc52.png)圖 4.3:靜態頁面控制器的類繼承關系 ## 4.4.5 用戶類 我們要自己定義一個類,結束對 Ruby 的介紹。這個類名為 `User`,目的是實現 [第 6 章](chapter6.html#modeling-users)用到的用戶模型。 到目前為止,我們都在控制臺中定義類,這樣很快捷,但也有點不爽。現在我們要在應用的根目錄中創建一個名為 `example_user.rb` 的文件,然后寫入[代碼清單 4.13](#listing-example-user) 中的內容。 ##### 代碼清單 4.13:定義 `User` 類 example_user.rb ``` class User attr_accessor :name, :email def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] end def formatted_email "#{@name} <#{@email}>" end end ``` 這段代碼有很多地方要說明,我們一步步來。先看下面這行: ``` attr_accessor :name, :email ``` 這行代碼為用戶的名字和電子郵件地址創建“屬性訪問器”(attribute accessors),也就是定義了“獲取方法”(getter)和“設定方法”(setter),用來取回和賦值 `@name` 和 `@email` 實例變量([2.2.2 節](chapter2.html#mvc-in-action)和 [3.6 節](chapter3.html#mostly-static-pages-exercises)簡介過)。在 Rails 中,實例變量的意義在于,它們自動在視圖中可用。而通常實例變量的作用是在 Ruby 類中不同的方法之間傳遞值。(稍后會更詳細地介紹這一點。)實例變量總是以 `@` 符號開頭,如果未定義,值為 `nil`。 第一個方法,`initialize`,在 Ruby 中有特殊的意義:執行 `User.new` 時會調用這個方法。這個 `initialize` 方法接受一個參數,`attributes`: ``` def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] end ``` `attributes` 參數的默認值是一個空哈希,所以我們可以定義一個沒有名字或沒有電子郵件地址的用戶。(回想一下 [4.3.3 節](#hashes-and-symbols)的內容,如果鍵不存在就返回 `nil`,所以如果沒定義 `:name` 鍵,`attributes[:name]` 會返回 `nil`,`attributes[:email]` 也是一樣。) 最后,類中定義了一個名為 `formatted_email` 的方法,使用被賦了值的 `@name` 和 `@email` 變量進行插值,組成一個格式良好的用戶電子郵件地址: ``` def formatted_email "#{@name} <#{@email}>" end ``` 因為 `@name` 和 `@email` 都是實例變量(如 `@` 符號所示),所以在 `formatted_email` 方法中自動可用。 我們打開控制臺,加載(`require`)這個文件,實際使用一下這個類: ``` >> require './example_user' # 加載 example_user 文件中代碼的方式 => true >> example = User.new => #<User:0x224ceec @email=nil, @name=nil> >> example.name # 返回 nil,因為 attributes[:name] 是 nil => nil >> example.name = "Example User" # 賦值一個非 nil 的名字 => "Example User" >> example.email = "user@example.com" # 賦值一個非 nil 的電子郵件地址 => "user@example.com" >> example.formatted_email => "Example User <user@example.com>" ``` 這段代碼中的點號 `.`,在 Unix 中指“當前目錄”,`'./example_user'` 告訴 Ruby 在當前目錄中尋找這個文件。接下來的代碼創建了一個空用戶,然后通過直接賦值給相應的屬性來提供他的名字和電子郵件地址(因為有 `attr_accessor` 所以才能賦值)。我們輸入 `example.name = "Example User"` 時,Ruby 會把 `@name` 變量的值設為 `"Example User"`(`email` 屬性類似),然后就可以在 `formatted_email` 中使用。 [4.3.4 節](#css-revisited)介紹過,如果最后一個參數是哈希,可以省略花括號。我們可以把一個預先定義好的哈希傳給 `initialize` 方法,再創建一個用戶: ``` >> user = User.new(name: "Michael Hartl", email: "mhartl@example.com") => #<User:0x225167c @email="mhartl@example.com", @name="Michael Hartl"> >> user.formatted_email => "Michael Hartl <mhartl@example.com>" ``` 從[第 7 章](chapter7.html#sign-up)開始,我們會使用哈希初始化對象,這種技術叫做“批量賦值”(mass assignment),在 Rails 中很常見。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看