天天看點

《Effective Ruby:改善Ruby程式的48條建議》一第14條:通過protected方法共享私有狀态

本節書摘來自華章出版社《effective ruby:改善ruby程式的48條建議》一書中的第2章,第2.9節,作者 [美]彼得 j.瓊斯(peter j. jones),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

封裝是面向對象程式設計中的主要準則之一,它是指一個對象的内部實作僅可被内部通路,不可被外部通路。這使得我們可以建構一層壁壘。一方面,暴露那些允許别人使用的外部接口。另一方面,可以靈活地改變其内部的實作而無需為破壞類的外部行為而擔憂。在ruby語言中,這種壁壘可能理論味十足,但至少是存在的。

以執行個體變量為例——它們預設是私有的。如果不借助任何後門式的元程式設計技巧,它們隻能在執行個體方法内部被通路。若将其暴露到外部,則需定義被稱為accessor的方法。這是個好消息。你可以放心地在執行個體變量中存儲内部狀态而無需擔心它可能會意外地成為公共接口的一部分。這樣的封裝在多數情況下都是極好的,但有一種情況它們反而會成為阻礙。因為某些時候一個對象需要通路另一個對象的内部狀态。考慮下面的

例子:

《Effective Ruby:改善Ruby程式的48條建議》一第14條:通過protected方法共享私有狀态

方法Widget#overlapping?會檢查其本身是否和另一個對象在螢幕上重合。widget類的公共接口并沒有将螢幕坐标對外暴露;它們的具體實作隐藏于内部。看上去突破封裝的壁壘的唯一方法就是使用一些元程式設計技巧了。但這種方法有其自身的缺點。

通常情況下,當設計一個具有内部狀态的類時,正确的做法是盡可能減少對該狀态的直接通路,而是使用最小數量的通路方法。這樣做既提升了可測試性也降低了變更可能對系統造成的破壞。把對于狀态的操作隐藏于私有方法中也有助于優化程式,如優化記憶體(參考第48條)。為了提升可維護性,更好的做法是讓widget類的螢幕坐标資訊保持私有,即便是對于overlapping?方法也應如此。你可能希望去寫一個私有方法來充當通路螢幕坐标的接口,但在這種情況下并不适用。

在ruby語言中,私有方法的行為和其他面向對象的程式設計語言中不太相同。ruby語言僅僅在私有方法上加了一條限制——它們不能被顯式接收者調用。無論你在繼承體系中的哪一級。隻要你沒有使用接收者,你都可以調用祖先中的私有方法。但是你不能調用另一個對象的私有方法,即便是調用者和接收者隸屬于同一個類。如果你使用一個私有方法來通路螢幕坐标,你仍然無法在overlapping?方法中使用它們。還好,ruby提供了針對這種情況的解決方式:protected方法。

接收者無法使用私有方法,但可以使用protected方法。但有一個例外,而且它還有些複雜。被接收者調用的方法必須處于調用者繼承體系中的哪一級,隻要調用者和接收者都可以通過繼承體系共享同一個執行個體方法,那麼調用者就可以調用接收者上的方法。調用者和接收者并不需要是同一個類的執行個體,但是它們必須共享一個定義了該方法的超類。

對于Widget類來說,可以定義一個暴露私有螢幕坐标的方法,但并不通過公共接口來實作。其實作方式是聲明該方法為protected,這樣我們既保持了原有的封裝性,也使得overlapping?方法可以通路其自身以及其他傳入的widget執行個體的坐标。這樣就無需元程式設計的黑魔法了!

《Effective Ruby:改善Ruby程式的48條建議》一第14條:通過protected方法共享私有狀态

這正是設計protected方法的原因——在相關類之間共享私有資訊。

要點回顧

通過protected方法共享私有狀态。

一個對象的protected方法若要被顯式接收者調用,除非該對象與接收者是同類對象或其具有相同的定義該protected方法的超類。