天天看點

《程式設計導論(Java)·6.1封裝性》-什麼是封裝

1.通常的解釋

你可以不看這一部分的東西。下面摘錄的是一些通常的說法:百度百科面向對象技術

【面向對象技術的基本特征主要有抽象性、封裝性、繼承性和多态性。

封裝(Encapsulation)就是把對象的屬性和行為結合成一個獨立的機關,并盡可能隐蔽對象的内部細節。封裝有兩個含義:一是把對象的全部屬性和行為結合在一起,形成一個不可分割的獨立機關。對象的屬性值(除了公有的屬性值)隻能由這個對象的行為來讀取和修改;二是盡可能隐蔽對象的内部細節,對外形成一道屏障,與外部的聯系隻能通過外部接口實作。

封裝是通過限制隻有特定類的對象可以通路這一特定類的成員,而它們通常利用接口實作消息的傳入傳出。舉個例子,接口能確定幼犬這一特征隻能被賦予狗這一類。通常來說,成員會依它們的通路權限被分為3種:公有成員、私有成員以及保護成員。有些語言更進一步:Java可以限制同一包内不同類的通路;C#和VB.NET保留了為類的成員聚集準備的關鍵字:internal(C#)和Friend(VB.NET);Eiffel語言則可以讓使用者指定哪個類可以通路所有成員。 

封裝的資訊隐蔽作用反映了事物的相對獨立性,可以隻關心它對外所提供的接口,即能做什麼,而不注意其内部細節,即怎麼提供這些服務。例如,用陶瓷封裝起來的一塊內建電路晶片,其内部電路是不可見的,而且使用者也不關心它的内部結構,隻關心晶片引腳的個數、引腳的電氣參數及引腳提供的功能,利用這些引腳,使用者将各種不同的晶片連接配接起來,就能組裝成具有一定功能的子產品。

封裝的結果使對象以外的部分不能随意存取對象的内部屬性,進而有效地避免了外部錯誤對它的影響,大大減小了查錯和排錯的難度。另一方面,當對象内部進行修改時,由于它隻通過少量的外部接口對外提供服務,是以同樣減小了内部的修改對外部的影響。同時,如果一味地強調封裝,則對象的任何屬性都不允許外部直接存取,要增加許多沒有其他意義,隻負責讀或寫的行為。這為程式設計工作增加了負擔,增加了運作開銷,并且使得程式顯得臃腫。為了避免這一點,在語言的具體實作過程中應使對象有不同程度的可見性,進而與客觀世界的具體情況相符合。

封裝機制将對象的使用者與設計者分開,使用者不必知道對象行為實作的細節,隻需要用設計者提供的外部接口讓對象去做。封裝的結果實際上隐蔽了複雜性,并提供了代碼重用性,進而降低了軟體開發的難度。

(Stanley B.Lippman / Josee Lajoie / Barbara E.Moo .C++ Primer:人民郵電出版社,2010)】

2.學習思路

《程式設計導論(Java)·6.1封裝性》,按照如下思路介紹封裝性: Parnas原則-類的接口-封裝。 (1)封裝(Encapsulation)是指将對象的全部屬性和方法包裹起來,形成一個不可分割的整體。在OO中,封裝的一句話解釋,關鍵詞是“整體”或資料抽象。 “整體”,用細胞膜來形容封裝是很好的。

《程式設計導論(Java)·6.1封裝性》-什麼是封裝

關于“整體”似乎沒有太多好說的東西。類,你 總得讓它成為一個軟體實體,當然要搞成一個“整體”。用一對大括号将類的成員和非成員包裹起來,僅保留外界能夠通路的接口。 包裹動作和形成接口是一個硬币的兩面。包裹動作是程式勞工的不太繁重的體力勞動——實際上對于方法也有同樣的包裝動作——用一對大括号将各種語句包裹成一個整體。

(2)封裝這個概念,在C語言中,用于函數的封裝,因而有Parnas原則——接口與實作分離;在面向對象技術中,則用于形成類的接口,因而是Parnas原則的推廣。 需要注意的是: 在早期,人們将方法層面的接口與實作相分離稱為資訊隐藏。例如在全世界程式員奉為經典的《人月神話(二十周年紀念版)》中,Brooks以《關于資訊隐藏,Parnas 是正确的,我是錯誤的》作為紀念版一節的标題。Brooks的此舉令人敬仰。 本書中, 資訊隐藏,指外界是否能夠通路;而接口與實作的分離,指程式員是否願意了解方法的實作。 換言之,形成類的接口即資訊隐藏屬于可見性議題;而程式員通常對( 不被隐藏的)接口的實作視而不見。

3.變化與封裝

正如前言中提到的,“面向對象的封裝、繼承、動态綁定(方法改寫)、多态等知識,在程式設計語言教學中并沒有太多的理論含義(程式設計語言課程不涉及程式設計語言設計和形式化的内容)”,yqj2065比較輕視這幾個概念。是以,封裝寫的很少。

《Think in java·第 5 章隐藏實施過程》的第一句話【将發生變化的東西與保持不變的東西分隔開】,理論上,對于“變化”,與封裝一毛錢的關系都沒有。 要點是,何謂變化?

  • 如果你的“變化”,是指資訊隐藏部分的代碼的自由修改,那麼能夠說封裝了“變化”,這是Parnas原則(接口與實作分離)的必然後果。對于資料抽象或類A,使用者隻能夠與其接口互動。隻要A保證其接口不變,其内部實作就可以自由地演化。是的,這種自由源于封裝,破壞這種自由就破壞了A的封裝性。
  • 對于(需求)變化的封裝,又稱開放封閉原則OCP。這裡的變化,指原來需求要求處理貓和狗,後來需求要求處理牛、馬等。為了封裝這種“變化”,我們的代碼中将要處理的,是“動物”類型。(另一方面,OCP所言的對修改封閉,也不幹涉被隐藏的實作可以自由演化)。
  • 類的接口能否修改?關于庫(例如其中有類A)的設計,依賴庫的程式B要求庫是向下相容的。這裡,程式B依賴的是A的接口。不論庫的建立者想怎樣自由地進行修改與改進A,前提是不得修改A的接口。否則庫不是相容的。A的設計者不可能、也不會“考慮”該服務(的方法名、參數)未來是否保持不變或改變。如果你使用過一些修改過接口的第三方庫,你肯定感受頗深。
  • 以JDK中的類為例,類的接口變化時采用的方式:

    列舉 不贊成使用(Deprecated)的類的接口/方法

    Java8 的預設方法為Java接口的變化 提供後悔藥。

最後,“封裝變化”Find what varies and encapsulate it. 是面向對象設計/設計模式的中心思想,但是與封裝性是兩個概念。

《程式設計導論(Java)·6.1封裝性》-什麼是封裝

繼續閱讀