這是在“北京設計模式學習小組”裡討論的一個問題,關於是否可以直接公開類的屬性欄位?問題起源於對單例模式的討論。由於行文的流暢和理解的必要,這裡作了一些整理。
【註:因為擔心涉及到隱私,這裡未公開各位發言者的暱稱和QQ號。也未向各位發言者徵求是否同意被引用的請求。若您看到文章後覺得有不妥之處,請在評論處留言,我會及時聯絡您並作出相應處理 。謝謝!】
Y: 如果把一個非單例的概念弄成一個單例設計,回退起來是很痛苦的,反過來還好處理一些。單例確實就是全域變數,是良好命名過的全域變數
HW:一般我都直接靜態欄位
Y:直接暴露欄位是極其不好的,這不需要理由解釋
W:寫getter和setter與直接暴露沒有區別不暴露欄位用反射也一樣能訪問
Y: 你說的簡單資料結構,不包含行為的類是例外
W: 所以欄位是否暴露不取決於安全性而是取決於可讀性
Y: 代碼不是用來破壞,而是用來維護的
W: 我說這個的意思也是為了說明,類的成員是否公開主要應該取決於可讀性,而不是安全性比如person.age與person.getAge()相比我更傾向於公開
Y: person.age與person.getAge()如果你硬覺得前者比後者有可讀性的話,我不反對
e5Max:
支援!
我想你的意思可能是說,如果一個類除了setter 和getter 之外沒有別的方法,那麼還不如直接將成員變數公開。
W: 是這個意思
Y: person.age與person.getAge() 後者比前者具有可維護性, 如果你的Person裡有一個生日屬性,那麼將來你可能會重新設計Age。 而用了前者,你就沒得設計
W: 你可以參照 playframework.org的開源項目orm早就放棄了hibernate的getter/setter 而是全用屬性變數
Y:
不要公開欄位,盡量...
HW: 所以說不要隨便公開類裡的方法或屬性
W: 代碼可讀性大為提高關於age那個增加生日後去掉age欄位變為age()方法
HW: 你可以做個結構體或靜態工具類
W: 而不是用get
e5Max: 《C++沉思錄》裡面就有一個例子說明什麼時候可以將成員公開,—— 這是作者C++實用主義的主張!
HW:
有時候模式是會變成反模式的
有重構解決設計問題
先用靜態變數
不對 是屬性
然後以後需要的話重構就可以了
最大限度的避免過度,設計
......
Y:
有這麼一個例子嗎?能簡要介紹下不,我忘記了,時間太久了
e5Max:
@懷化-英界爾-C
《C++沉思錄》這本書確實有很多將 成員屬性 定義為 public 的範例程式碼。
書中有一個地方也專門提及了這個問題,當然不是專門的論述。我找找,看能否發個看看啊。
e5Max:
沒錯! 但並不意味著不要!有些例外情況下還是可以的,而且也是有必要的。
以下是《C++沉思錄》電子版的:
Y: 太好了,我的意見是你十分明確你要做的事情後,就去做,不用受別的約束,前提是你確實明確
e5Max: 對! 就是我們明確自己這樣做的好處大於益處,至少不會帶來任何破壞!
由此我們大概可以得出如下結論:
一、盡量不要直接暴露類的屬性欄位,這是極其不好的,甚至都不需要理由解釋。
二、盡量但並不意味著絕對不要!
那麼哪些情況下可以直接暴露類的屬性欄位呢?
1)簡單的資料結構,不包含行為的類,例如除了setter 和 getter 之外沒有別的方法,那麼這種情況下直接暴露類的屬性欄位是可以接受的。
2)C++類的巢狀型別,對於類的巢狀型別,在不影響可讀性的情況下,我們允許直接暴露它的屬性欄位,因為它的客戶只有類本身,所以不存在訪問安全性。
【關於Pimpl慣用法:Pimpl慣用法通過將私人資料和函數放入到定義在實現檔案中的一個單獨類型中,然後在標頭檔中對這個類型進行前置聲明並儲存一個指向該類型的指標,隱藏了相關細節內容。類的建構函式分配Pimpl類型,而解構函式則釋放它。這就消除了實現細節和標頭檔的相關性。】
3)是否還有第三種情況?我想是有的。
總之,正如英界爾同學所說,如果你十分明確你要做的事情,就去做,不用受別的約束,前提是你確實明確。我們要明確自己這樣做的好處大於益處,至少不會帶來任何破壞!