本節書摘來自華章出版社《effective ruby:改善ruby程式的48條建議》一書中的第2章,第2.4節,作者 [美]彼得 j.瓊斯(peter j. jones),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視
說到命名規則,ruby給了我們很大的自由。肅然不像lisp那樣的語言一樣自由,但ruby允許我們在方法末尾使用三個字母數字以外的符号:“?”、“!”和“=”。三個字元中的兩個具有純粹的美感,而另一個在ruby中有特殊的含義。
如你所知,以問号結束的方法名既不會改變任何事情,也不會使ruby對它特殊對待。這僅僅是一個為ruby程式員所接受的命名慣例,以此來說明方法将傳回一個布爾值。(要知道什麼是ruby中的布爾值,請參見第1條。)這個命名慣例并不是強制的,并且以“?”結尾的方法仍然可以傳回你想要的任何類型的值。感歎号也是類似的,如果有所不同,也隻是模糊一點。感歎号常常意味着方法會改變接收者,也可以是對你的示警,表示存在潛在的有害行為。這兩種情況都是ruby程式員指南中不提倡的。以等号結尾的方法則完全不同。
以“=”結尾的方法将變為setter方法,并允許你使用漂亮的文法來調用它。典型場景是,這類方法接受一個參數,改變一些内部狀态,并傳回它們的參數。這意味着setter方法也能被用作左值(指派語句的左側部分)。

即使從技術上講“=”是方法名的一部分,ruby卻允許我們在等号和方法名的其他部分之間輸入空格。這看似是變量指派,但實質上僅僅是一個普通的方法調用。當你加上括号并去掉空格時你會更清楚地看到這一點。
你可能不曾親手定義過這些setter方法,但你一定間接地定義過。因為ruby有一些幫助方法能幫我們做這個事情。attr_writer和attr_accessor都像之前的例子一樣定義了類似value=這樣的setter方法。這樣的迂回可能會讓你有點困惑,這也是我提起它們的原因。隻要你記住這些幫助方法做的事情以及接下來的建議,任何困難都不是問題。
說到困難,我們得看看指派和setter之間的差別。由于setter方法的調用看似變量指派,是以很容易混淆它們。看下面這個例子:
把initialize方法體中的内容當作對counter=方法的調用也不是毫無道理,這也是很多人所假設的。但這當然是不對的,那隻是個簡單的變量指派呀。initialize方法建立了一個新的局部變量counter,并将其指派為0,然後又在作用域結束的時候丢棄了對這個變量的引用。當你仔細思考,會發現這顯然不是我們想做的。(如果你像第5條建議的那樣啟用了警告,ruby将會在出現這個錯誤時提醒你。)
ruby在對變量指派和對setter方法調用時的解析是有差別的。差別在于,ruby調用setter方法時要求存在一個顯式接收者。為了調用setter方法,而非建立一個變量,你需要預先指定方法名的接收者。是以,在執行個體方法中調用方法counter=,你需要使用self充當這個接收者。
通過使用self作為接收者,ruby将正确地解析你的代碼并調用counter=這個setter方法,而非建立一個新的變量。你可能會想,如果在調用方法時在等号和方法名的其他部分間不加等号,并用括号包住參數也許可以避免使用self(即,counter=(0)),然而事與願違,這是無效的。我們在這裡被迫顯式地使用self作為接收者,但這導緻了另一個問題。
被解析規則坑過的程式員傾向于在它們的代碼中過分使用不必要的self接收者,這會使代碼顯得淩亂。在每個方法調用前都加上self從技術角度來說沒有問題,但顯然它降低了代碼的可讀性。這顯然是ruby程式員中普遍存在的代碼的壞味道。看看你能否在下面的代碼中找到無須使用self的地方:
顯然,full方法中的self是備援的。由于沒有調用setter方法,你可以安全地移除self并依靠那些簡單的解析規則。當ruby遇到類似f?irst或last的辨別符時,它會檢查目前作用域中是否存在同名變量。如果不存在,它會把這個辨別符作為方法名再次查找。在本例中,對于name類裡的兩個屬性,ruby通過早先的attr_accessor找到了它們的getter方法。這裡有另一個版本的full方法,與之前的方法做了相同的事情,但去除了噪音:
到此為止,你應該對setter方法的特殊性确信無疑了。如果不想碰到些驚喜,記得調用時指定一個接收者。但是千萬别是以讓它愚弄了你,并不是所有方法都需要接收者,尤其是在執行個體方法中調用時。對于每個其他類型的方法,如果沒有顯式指定方法接收者,ruby将自動使用self作為接收者。
要點回顧
setter方法在調用時需要顯式的接收者。沒有接收者時,會被ruby解析為變量指派。
在執行個體方法中調用setter方法時,使用self作為接收者。
在調用非setter方法時,不需要顯式指定接收者。換句話說,不要使用不必要的self,那會弄亂你的代碼。