2014-08-29 102 views
4

在Ruby中定義訪問者時,可能會在簡潔(我們都愛)和最佳實踐之間產生緊張關係。Ruby私有和公共訪問者

例如,如果我想揭露上的一個實例的值,而是從更新它禁止任何身外之物,我能做到以下幾點:

class Pancake 
    attr_reader :has_sauce 

    def initialize(toppings) 
    sauces = [:maple, :butterscotch] 
    @has_sauce = toppings.size != (toppings - sauces).size 
... 

但是突然我使用的是原始實例變量,這讓我抽搐。我的意思是,如果在未來的日期設置之前需要處理has_sauce,我可能需要做更多的重構,而不僅僅是覆蓋訪問器。來吧,原始實例變量?布萊什。

我可以忽略該問題並使用attr_accessor。我的意思是,任何人都可以設置屬性,如果他們真的想要;畢竟,這是Ruby。但是後來我失去了數據封裝的想法,對象的接口定義不好,系統可能更加混亂。

另一種解決方案是定義在不同的訪問修飾符一對訪問器:

class Pancake 
    attr_reader :has_sauce 
    private 
    attr_writer :has_sauce 
    public 

    def initialize(toppings) 
    sauces = [:maple, :butterscotch] 
    self.has_sauce = toppings.size != (toppings - sauces).size 
    end 
end 

哪幹得不錯,但是這是樣板的一個簡單的訪問,並坦率地說,一大塊:EW。

那麼是否還有更好的Ruby方法?

+0

**哪些工作可以完成**錯!你的initialize()方法中的has_sauce是一個局部變量 - 不是一個實例變量。你甚至沒有測試你的代碼(它也有另一個錯誤)。 **我的意思是,如果我在未來的日期設置之前需要處理has_sauce,那麼我可能需要做更多的重構,而不是重寫訪問器。**通過設置setter來設置實例變量是一種很好的做法,並且使用兩個訪問修飾符是實現只讀實例變量的方法。 – 7stud 2014-08-29 17:45:26

+0

未經測試的代碼最深切的歉意,並很好的發現。現在修復,fwiw。另外,我很高興你同意這是一個很好的做法,但是這種實現感覺有點不好意思。可能只是我。 – 2014-09-02 09:49:39

回答

3

attr_reader等只是方法 - 沒有理由,你可以定義變量爲自己使用(和我分享你的看法)例如:

class << Object 
    def private_accessor(*names) 
    names.each do |name| 
     attr_accessor name 
     private "#{name}=" 
    end 
    end 
end 

然後使用private_accessor,你會attr_accessor(我想你雖然需要比private_accessor更好的名字)

+0

這更像它!我正在考慮這樣做,但我想在更改對象之前探索所有其他選項......我傾向於嘗試不干擾語言的基本原則,除非它非常冒險和/或效率不高。或者除非我真的很想。 ;) – 2014-09-02 09:53:43

+0

另外,我無法想象訪問者的好名字!也許這就是爲什麼它不在語言中。我確實在我的旅行中找到了這個:https://github.com/dbrady/scrapbin/blob/master/scraps/scoped_attr_accessor.rb,它具有大型測試套件的優點,但缺點是需要兩個訪問器來組成要求的結果。 – 2014-09-02 09:57:26

+0

**和我分享你的感受**真的嗎?因此,以你的敏感性寫作:'私人attr_writer:has_sauce'就像黑板上的指甲,但寫'class << Object def private_accessor(* names) names.each do | name | attr_accessor name private「#{name} =」 end end end private_accessor:has_sauce'感覺絲綢般光滑嗎? – 7stud 2014-09-02 19:59:52

2

private可以採取象徵阿根廷,所以......

class Pancake 
    attr_accessor :has_sauce 
    private :has_sauce= 
end 

class Pancake 
    attr_reader :has_sauce 
    attr_writer :has_sauce; private :has_sauce= 
end 

等等

但是,什麼是與 「原始」 的實例變量的事?它們是你的實例的內部;唯一可以通過名稱調用它們的代碼是pancake.rb之內的代碼,這都是你的。事實上,他們開始於@,我認爲這讓你說「blech」,是什麼使他們私密。如果你願意,可以把@想象成private的簡寫。

至於處理,我認爲你的直覺是好的:如果可以的話在構造函數中進行處理,或者在必要時在自定義存取器中進行處理。

+0

我沒有解釋我不喜歡在這種情況下使用實例變量。但爲了進一步解釋它,我使用的一個原則是:如果你有一個訪問器,使用訪問器而不是它隱藏的原始變量或機制。原始變量是實現,訪問者是接口。我是否應該忽略界面,因爲我是內部的實例?如果它是一個attr_accessor,我不會。 – 2014-08-29 16:25:43

+0

但謝謝你的關注。 – 2014-08-29 16:27:16

+1

然後'attr_accessor:bar;私人:酒吧=;'是你想要的。 – AlexChaffee 2014-09-02 14:41:45

0

直接在您的類中引用實例變量沒有任何問題。無論如何,無論您是將這些方法設置爲公開還是私人,attr_accessor都只是間接地做到這一點。

在這個特殊的例子,它可以幫助認識到toppings很可能要保存用於其它目的的屬性,並且has_sauce是一個「虛擬屬性」,該模型的特徵上基本配料屬性這是相關的。

像這樣的東西可能會覺得更清潔:

class Pancake 
    def initialize(toppings) 
    @toppings = toppings 
    end 

    def has_sauce? 
    sauces = [:maple, :butterscotch] 
    (@toppings & sauces).any? 
    end 
end 

取決於你是否不暴露attr_accessor :toppings爲好。如果你只是扔掉配料,那麼你的班級就少一個Pancake和更多的PancakeToppingDetector;)

+0

我看到一個'虛擬屬性'是我們在使用框架,ORM等時被迫執行的東西。有些東西可以讓我們將代碼注入到任何元編程的路徑中。在這樣一個簡單的PORO中,我看不出它有什麼意義;這只是一種方法。 :) – 2016-04-08 15:27:34

+0

隨着整個'在整個代碼中使用實例變量'的事情,我會告訴你在這: https://github.com/dbrady/scoped_attr_accessor 有人爲我完成了工作。 :) – 2016-04-08 15:28:21

+0

此外,桑迪梅斯在POODR中提到這一點。我記得,她還主張在方法中包裝裸露的實例變量,即使它們只用於內部。它有助於避免以後的重構。 – 2016-04-08 15:30:01