Getter、Setter的用與不用
文章推薦指數: 80 %
有人將罪過推給了JavaBean對Getter、Setter做了規範,然而,若問題根源的確是如此,那麼,直接支援特性(Properties)存取語法的語言,又是怎麼一回事呢?
移至主內容
文/林信良
|
2015-10-05發表
在Java界,有個該不該使用Getter、Setter的老問題,不單是初學者經常覺得多此一舉,就連老手們偶而也會從封裝、維護、抽象化等角度,戰上數回。
有人將罪過推給了JavaBean對Getter、Setter做了規範,然而,若問題根源的確是如此,那麼,直接支援特性(Properties)存取語法的語言,又是怎麼一回事呢?
直接來對Getter、Setter吧!
如果在類別中,直接建立了一個public的值域(Field),那麼,馬上就會有人說這樣的設計不好,破壞了封裝,應該將值域設為private,然後加上一對Getter、Setter,例如類別中若有個name值域,就會成為這副模樣:
privateStringname;
publicvoidsetName(Stringname){this.name=name;}
publicStringgetName(){returnname;}然而,老實說吧!這種寫法有什麼封裝能力嗎?跟類別中直接寫個publicStringname有什麼不同呢?主張一開始就要加上Getter、Setter的人會列舉優點,像是方法可以有public、protected、private等存取權限、未來可於方法中加上存取控制(驗證、延遲加載、生命週期等)、可以改變值域名稱、可以重新定義(Override)、有的框架預期有這樣的命名慣例、可以用於Lambda方法參考,甚至是除錯時加上中斷點很方便之類的理由。
現代功能較齊全的IDE,可以自動產生Getter、Setter,似乎加強了這類觀點,如果嫌一堆Getter、Setter使得程式碼變醜、變冗長了,還可以考慮使用Lombok這類框架,以標註方式來提供Getter、Setter,為了未來的種種可能性,為每個值域都產生一對Getter、Setter似乎就沒錯了,不過,若試著將這種想法帶到具有特性存取語法的語言中實現,像是在Python、Ruby、JavaScript(ECMAScrpt5)等,似乎又有點怪異了,在這類語言中,通常是在必要情況下,才會使用特性存取語法加以定義。
需要的是固定存取原則?
顯然地,Java的Getter、Setter語法來自JavaBean規範,有些框架也經常要求Getter、Setter的命名方式,然而,對於支援特性存取語法的語言,定義特性時的語法本身,不也是個規範嗎?因而,問題根源並不在於JavaBean規範,或者一些框架的要求助長了醜陋的Getter、Setter,應當思考的是,當需要為private的值域,加上一對Getter、Setter,是否在思考的是固定存取原則(Uniformaccessprinciple)的需求?
舉例來說,在Python,若一開始於類別的__init__(self,radius)中,定義了self.radius=radius,那麼,使用者在建立實例並指定給ball之後,就可以透過ball.radius取值、透過ball.radius=2.31設值,若將來打算在設定時加上驗證、生命週期等功能,可以使用@property、@property.setter定義類別的特性:
@property
defradius(self):
returnself.__radius
@radius.setter
defradius(self,radius):
#一些存取控制
self.__radius=radius 那麼,原先使用了ball.radius取值、ball.radius=2.31設值的客戶端,並不需要修改。
許多提供特性存取語法的語言,都能達到這類功能,這樣的實現稱為固定存取原則,也就是提供客戶端服務時所使用的名稱必須固定,無論該服務是透過計算或既有的結果。
然而,Java並不具備這樣的能力,因此,為了保有未來實現固定存取原則的可能性,以應付stackoverflow上〈Whyusegettersandsetters?〉(http://goo.gl/6RnbUz)這類問題中提到的未來種種需求,為此就只好未雨綢繆地,事先建立Getter、Setter。
這應該是物件的特性嗎?
實際上,特性不等於值域,特性是對客戶端公開的協定,若為每個private值域建立一對公開的Getter、Setter,等於是「將值域直接對應為物件的特性」,有此類需求的一個場合,是將物件單純作為一種資料結構(或者稱為資料容器),針對這類值域就是特性的場合,為了保有未來實現固定存取原則的可能性,或者符合框架需求,直接存取private值域的Getter、Setter,並不會是多此一舉。
然而,未經思考,就直接為每個private值域,建立一對公開的Getter、Setter,就有很大的可能性會混淆了特性與值域的角色。
將代表物件內部狀態的值域,直接透過Getter傳回給客戶端,客戶端就有可能進一步操作值域參考之物件,例如,當值域實際為ArrayList,那麼直接透過Getter傳回,就有可能造成客戶端將ArrayList清空,而這並不是提供Getter的物件想要之結果,另一個可能性是客戶端取得值域後,進一步探索值域的值域,因而違反了迪米特法則,造成嚴重的相依性。
正因為值域不應對客戶端公開,因此是否保有固定存取原則實現可能性的考量,應是針對特性,而不是值域。
當Getter、Setter直接曝露值域時,應當問的是這個值域會是物件的公開特性嗎?若答案是否定的,那麼直接為值域建立Getter、Setter就是破壞封裝,而且會造成客戶端取得值域後,將邏輯寫在其他地方,而不是讓邏輯處於值域所在的物件之中。
這也是〈Whygetterandsettermethodsareevil〉在探討的問題之一。
針對客戶端取得值域後,將邏輯寫在其他地方的問題,可思考Tell,Don'tAsk原則,也就是別查詢物件狀態後做些什麼,而是命令物件來做些什麼,這個原則和迪米特法則相似,如果流程中不斷透過某物件的Getter、Setter取值、執行某個邏輯、設值,那麼也許應該在物件上建立一個方法,直接命令物件完成任務,在《TheThoughtWorksAnthology》中,提到讓軟體設計更好的第九個練習是:「不要使用任何getters/setters/properties」,真正目的就是在遵循Tell,Don'tAsk。
別只是個GetterEradicators
然而,就像迪米特法則並非斤斤計較連續dot的數量,Tell,Don'tAsk原則也不是一看到Getter方法,就要感到不快,以免成了MartinFowler說的〈GetterEradicators〉(http://goo.gl/RF8q0R)。
就如同他在〈TellDontAsk〉(http://goo.gl/JaSbuA)中談到的,重點是將資料及資料相關的行為放在一起。
出現Getter並不見得就是壞事,如前面談到,物件也許單純作為一種資料結構,另一個應用場合是資源的隱藏或抽象化,例如也許從某視窗物件上透過getBackgroudColor取得Color,實際上,視窗物件內部也許是從JComponent值域或其他類型,取得顏色資訊後,包裝為Color,客戶端並不得而知。
類似地,看到Setter也不見得是壞事,除了當作是一種公開可設定的特性之外,如果物件確實依賴在另一個物件上,那Setter接受物件並直接設定給值域,也沒什麼不對(當然,設計上可以依賴在公開介面而非實作)。
問題的根源,從不在於是否使用了Getter、Setter(這也不僅是Java中才會有的問題),是的!有些框架確實要求要有Getter、Setter的命名規範,不過,這些框架真正要求的,並不是要曝露物件的值域,而是這些物件要有框架要求的公開特性,反過來說,如果沒有思考特性與值域的差別,就算曝露值域的方法不是被命名為getXXX、setXXX,或者是在其他語言中,使用語法定義特性直接曝露值域,也有極大的可能性破壞了封裝!
專欄作者
林信良
因在網路上經營「良葛格學習筆記」(openhome.cc)而聞名,曾任昇陽教育訓練中心技術顧問、甲骨文教育訓練中心授權講師,目前為自由工作者,專長為技術寫作、翻譯與教育訓練。
喜好研究程式語言、框架、社群,從中學習設計、典範及文化。
閒暇之餘記錄所學,技術文件涵蓋C/C++、Java、Ruby/Rails、Python、JavaScript、Haskell等多個領域。
熱門新聞
伊萊克斯旗下微波爐軟體更新錯誤,以為自己是蒸烤爐
2022-03-22
俄國殭屍網路程式CyclopsBlink瞄準華碩路由器
2022-03-18
微軟更新自有Linux發行版
2022-03-21
Fintech周報第209期:玉山金控揭露今年科技發展策略,將來銀行3月底開業,純網保最快8月開放申請
2022-03-20
維護者破壞熱門node-ipc套件抗議俄羅斯入侵烏克蘭
2022-03-21
微軟再示警IE11將在6月停用,企業用戶應及早汰換
2022-03-21
Lapsus$宣稱成功駭入微軟AzureDevOps程式庫
2022-03-22
0patch再度出手修補微軟修補不全的Windows權限擴張漏洞CVE-2021-34484
2022-03-22
Advertisement
專題報導
烏克蘭戰爭的科技衝擊
跟Google學ML系統SRE
大型企業IT如何技術出海
科技翻轉照護在榮家
別再使用PPAP傳檔!
更多專題報導
延伸文章資訊
- 1Getter、Setter的用與不用
有人將罪過推給了JavaBean對Getter、Setter做了規範,然而,若問題根源的確是如此,那麼,直接支援特性(Properties)存取語法的語言,又是怎麼一回事呢?
- 2Java Getter和Setter:基礎、常見錯誤和最佳實踐 - 每日頭條
在Java中,getter和setter是用於獲取和更新變量值的兩種傳統方法。 下面的代碼是一個帶有私有變量和一些getter/setter方法的簡單類的例子: public class ...
- 3建立getter 和setter - IBM
建立getter 和setter. 這個對話框可選取要建立的getter 和setter 方法。 您可從程式檔功能表或選定欄位或類型的快速功能表中使用產生Getter 和Setter,或是類型中...
- 4Java 入門指南- getter 與setter - 程式語言教學誌
Java 入門指南- getter 與setter ; 封裝, encapsulation ; 物件, object ; 類別, class ; 宣告, declare.
- 5getter - JavaScript - MDN Web Docs
儘管可以用getter 與setter 的關聯建立虛擬屬性的類型,但getter 無法被綁定到同時擁有實際數值的屬性。 使用 get 語法時,請注意以下情況:. 可以擁有 ...