Day12 -變幻無窮的變數:class variable,class instance variable與instance variable

前情提要:面試

 

第12天,往細節探索去!昨天咱們講到broc是有名字的內存塊物件,可儲存變數;lambda是一種method方法,嚴格檢查參數數目。今天想要更深地討論變數:)對象

 

Ruby經典面試題目#12繼承

Ruby的類別變數與類別實體變數,與實體變數有何不一樣?What is difference between class variable,class instance variable and instance variable?內存

 

咱們曾在第四天時討論過類別方法和實體方法(leafor)。get

 

還記得我下的這個結論:it

 

若是要將實體方法,運用在某個客製化的實體,就使用instance method;class

若是某個方法並不會和某個特定的實體變數綁在一塊兒,就使用class method。lambda

實體變數instance variablesso

實體變數是一個比較好理解的概念,來舉個例子吧:程序

我想把天天跑步的習慣RunDaily寫成class,爲了持續維持好習慣,方法有兩個:早上跑morning_run或者晚上跑evening_run,若是想早上跑(morning run),實體變數@mr會帶入參數5km,晚上(evening)跑的話,@ er帶入10km。

 

今天是第12天了~

 

咱們創造出day12的以下:

 

class RunDaily

def morning_run(km)

@mr = km

end

def evening_run(km)

@er = km

end

end

 

day12 = RunDaily.new

p day12.morning_run(5)

p day12

p day12.evening_run(10)

p day12

咱們能夠看到實體變數(instance variable)以@開頭,不須要先在class開頭宣告,緣由是:

 

Ruby的實體變數不是public,僅做用於於self指示的物件。除非明確提供其餘方法,

不然沒法從物件之外變動或查看。原文

 

5

#<RunDaily:0x000055e64755a770 @mr=5>

10

#<RunDaily:0x000055e64755a770 @mr=5,@er=10>

從輸出結果看到day12這個物件的方法是Rundaily,動態地加入了兩個實體變數mr和er。

 

實體變數的屬性(attribute)

物件的實體變數,就是物件的屬性(attribute),就算是同一個class的不一樣物件,其屬性也不一樣。

 

(還記得咱們在Ruby鐵人賽第一天提到,Ruby世界觀裏,萬物皆物件嗎?)

 

我很喜歡一種說法:每一天都是全新的一天,昨天、今天,明天都是不一樣的物件,獨立的個體:)

 

讓咱們來創造新物件,解釋不一樣物件的屬性。

 

假設我創造了明天這個新物件(第13天)Day13遇到休假日,因此早上一口氣跑了21km:

 

day13 = RunDaily.new

p day13.morning_run(21)

p day13

結果顯示此物件存在於不一樣的內存位置,並且變數也不一樣:

 

21

#<RunDaily:0x0000561a9376e1d0 @mr=21>

屬性存取器(attribute accessors)

就像咱們有時候會想知道每一個特定的日子分別跑了幾千米,或者是從新提取天天鐵人賽的文章內容究竟是什麼。

 

這時候可以讀取實體變數的屬性是很是重要的,讓咱們能夠更方便的讀取這些不一樣的物件(由於,凡走過必留下痕跡!就像翻開本身寫過的日記或鐵人賽同樣。)

 

案例一:Yesterday

如今來IronmanDairy類別裏寫一個屬性存取器(attribute accessors)的公開方法,讓咱們能夠設定(set_dairy)、取得(get_dairy)昨天Day12的鐵人賽文章標題:

 

class IronmanDairy

def set_dairy(title)#write dairy

@title = title

end

 

def get_dairy #read dairy

@title

end

end

 

day11 = IronmanDairy.new

p day11

 

day11.set_dairy(「Explain the difference between block,proc,lamdba.」)

day11.get_dairy #取出昨天文章的標題

 

p day11

 

日記day11物件被咱們讀取出來了:顯示出內存位置,及@title實體變數:

 

#<IronmanDairy:0x000055d4f44e2748>

#<IronmanDairy:0x000055d4f44e2748 @title=「Explain the difference between block,proc,lamdba.」>

案例二:Today

set_dairy和get_dairy方法雖然讓咱們易於瞭解屬性的寫入與讀取方式,但把細節拆解開來的代碼卻顯得過於冗長。

 

爲了產生同時具備讀Read+寫Write功能的實體變數(在這裏是@title),每次都要寫出這一對set_dairy和get_dairy方法,不是很累嗎?

 

(就像寫日記同樣,你不會把寫日記解釋爲:這是一組打開日記本,寫日記,而後再闔上日記本的動做。)

 

你就只是想。要。寫。日。記而已!

 

有沒有精簡的方法呢?

 

(你猜對了!只要仔細找一找手冊,Ruby裏一般都有方法!)

 

爲了秉持着每個今天都比昨天更好的精神,咱們提出改良版本:

 

假設咱們要寫第12天新文章day12,能夠利用寫入title=method,及取得titlemethod,查看文章標題,取代本來的set_dairy和get_dairy:

 

class IronmanDairy

def title=(title)#write dairy

@title = title

end

 

def title #read dairy

@title

end

end

 


day12 = IronmanDairy.new

day12.title =「class variable,class instance variable and instance variable」

p day12

p day12.title

結果印出:

#<IronmanDairy:0x00005648020c0828>

#<IronmanDairy:0x00005648020c0828 @title=「class variable,class instance variable and instance variable」>

注意,這裏的title=也是一個實體方法,咱們來用.instance_methods確認一下:

 

p IronmanDairy.instance_methods(false)#=> [:title=,:title]

案例三:Tomorrow

有沒有發現上面的代碼中,大量出現這個@title實體變數呢?若是想要更進一步簡化,能夠用attr_accessor方式改寫。

 

假設咱們要創一個明天Day13鐵人賽文章物件,直接把實體的屬性存取器attr_accessor:title,指定給symbol:title,加在類別的開頭便可:

 

class IronmanDairy

attr_accessor:title

end

 

day13 = IronmanDairy.new

p day13 #<IronmanDairy:0x00005579aee8bc00>

 

day13.title =「Still thinking…」

p day13 #<IronmanDairy:0x00005579aee8bc00 @title=「Still thinking…」>

 

p day13.title #「Still thinking…」

 

p IronmanDairy.instance_methods(false)#[:title=,:title]

從以上的[Yesterday,Today,Tomorrow]三個舉例,表明持續改良提取物件屬性的寫法,是否是可以對於實體變數有全方位的瞭解了呀?

 

類別變數class variable

類別變數在類別開頭,用@@定義,它是個危險的東西,由於全部的子類別中對類別變數的改動,都會影響其餘類別的變數。咱們用「雞兔同籠」的例子,來算算不一樣的動物各有幾隻腳:

 

class Animal

@@legs = nil #先預設動物的腳爲空值nil

def self.legs

@@legs

end

end

 

p Animal.legs # => nil

 

class Chicken < Animal #`雞`類別繼承`動物`類別

@@legs = 2

end

p Chicken.legs # => 2

p Animal.legs # => 2動物變2只腳了!

 

class Rabbit < Animal

@@legs = 4

end

 

p Rabbit.legs # => 4

p Animal.legs # => 4,動物又變4只腳了!

 


class Snake < Animal #籠子里加入一隻蛇

@@legs = 0 #蛇沒有腳!

end

 

p Animal.legs # => 0

p Snake.legs # => 0

p Rabbit.legs # => 0糟糕,爲何此次兔子沒有腳!~~被蛇吃掉了~~

爲了解決此問題,咱們來研究Ruby的類別實體變數,看看是否有更好的作法。

 

類別實體變數class instance variable

咱們在Day1中開宗明義地解釋面嚮對象語言的精髓:物件能夠具備類別和實體變數。既然類別也是一種物件,那「類別物件」固然能夠有「類別的實體變數」。咱們繼續「蛇兔同籠」的例子,舉例出三種變數大亂鬥:

 

class Animal #案例1: animal類別- class variable

@@legs = nil #設定類別變數爲nil

def self.legs

@@legs

end

end

 

p Animal.class_variables #印出類別變數:@@legs

p Animal.legs #類別變數:目前爲空值nil

 

p Animal.instance_variables # => []還沒有設定實體變數,因此沒東西

 

class Animal #案例2: animal類別- instance variable

attr_accessor:legs #設定實體變數

@legs = 0

end

 

p Animal.instance_variables # =>如今能印出實體變數:@legs

p Animal.legs #仍然是類別變數的空值nil(dcjwsc.com)

 

class Animal #案例3: animal類別- class instance variable

class << self;attr_accessor:legs end

#self在類別class裏,表明目前所在之處的animal類別(而不是案例1和案例2的同名類別唷)

 

@legs = 1 #設定「類別實體變數」,先預設爲1

end

 

p Animal.legs # => 1 #不是nil,不是0,而是1(類別實體變數!)

 

class Rabbit < Animal

@legs = 4

end

 

p Rabbit.legs # =>兔子4只腳

p Animal.legs # =>回到類別實體變數預設值1

 


class Snake < Animal

@legs = 0

end

p Snake.legs # =>蛇0只腳

p Rabbit.legs # =>兔子仍是4只腳!太好了~沒有被吃掉

p Animal.legs # =>回到類別實體變數預設值1

以上的舉例實實在在地證實我在這本書Effective Ruby中文版-寫出良好Ruby程序的48個具體作法Page 56查到的觀點:寧願用類別實體變數,也不要用類別變數。類別實體變數除了會打破類別及其子類別的共享關係(如同咱們舉的例子中,動物的腳數目隨意被改變),也提供更多的封裝,讓類別定義層級、或從類別方法裏,惟一可存取的是類別實體變數。

 

最後用比一比的表格來總結:)

 

類別變數class variable類別實體變數class instance variable實體變數instance variable

@@類別變數@類別實體變數@實體變數

在類別開頭設定可用attr_accessor的方式改寫可用attr_accessor的方式改寫

可用在類別方法或實體方法用在類別方法,不可用在實體方法用在實體方法

相關文章
相關標籤/搜索