來自:碼農翻身(微訊號:coderising)
Java 帝國第一代國王正式登基,百官前來朝賀。
大臣甲說道:“恭喜陛下登基,為了吸引更多程式員加入我國,臣建議儘快完善我們Java語言的OOP特性,封裝、繼承、多型。”
國王說:“一個個來,先說說封裝吧, 我們現在已經可以把資料和方法放到一個類中,接下來想辦法隱藏資訊,限制對他們的訪問了, 我聽說現在有不少人在使用C++, 能不能從它借鑒一下啊? ”
大臣乙對C++很有好感,他說:“陛下聖明,C++那裡有pubic, private,protected 等關鍵字,可以用於修飾屬性和方法,我們直接拿過來用得了。”
public class Person{
private String name;
private int age;
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
“如此甚好!” 國王表示贊許,不過他眼珠一轉,突然想到了早些年出現的Python, 他問道:“Python是怎麼處理封裝這個問題的?”
大臣甲從Python王國“倒戈”而來,懷著對故國的歉意,十分想把Python的語法帶來一點給Java,聽到國王這麼問起,趕緊說到:“Python的處理比較簡單,用兩個下劃線來表示私有的屬性和方法。”
class Person:
def __init__(self, name):
self.name = name
# 私有屬性
self.__age = 10
# 私有方法
def __secret(self):
return self.__age
p = Person("andy")
#可以訪問
print(p.name)
#私有屬性,無法訪問
print(p.__age)
#私有方法,無法訪問
print(p.__secret())
可是國王卻說:“嗯,這種方式挺簡單的嘛,用下劃線就實現了,很簡潔,我們能不能也這樣啊?”
大臣乙有點瞧不起這個指令碼語言,他趕緊說:“萬萬不可,陛下有所不知,這個Python啊,即使加了下劃線,也只是‘偽私有’的屬性和方法。”
“什麼是偽私有?”
“就是說外界依然有方法訪問他們!”
#用這種方法,依然可以訪問偽私有屬性和方法
print(p._Person__age) # 10
print(p._Person__secret()) # 10
“這算哪門子私有的屬性和方法? 一點都不純粹。” 大臣乙繼續補刀。
國王說:“好吧,那不學它了, 那JavaScript呢?他是怎麼實現封裝的?”
朝中的大臣們面面相覷,JavaScript? 這是什麼鬼?怎麼沒有聽說過?
(碼農翻身註:JavaScript的出現時間比Java要晚, 這個Java國王估計是穿越了。)
大臣甲看到自己的想法沒有“得逞”,又另闢蹊徑:“陛下,Python有module的機制,可以把多個類組織到一起,形成一個高內聚的單元,我們Java要不也這麼乾?”
國王瞪了大臣甲一眼,訓斥道:“不要什麼都學Python! 我們也得有點獨特的東西啊。對於如何組織class, 我們可以用package,一個package對應檔案系統的一個目錄,下麵可以有多個class檔案。 如果一個類沒有被public 修飾,那他只能被同一個package下麵的類訪問,其他package的類是訪問不到的。這個設計不錯吧?!”
國王甚為得意。
同一個package下有三個類A,B,C, 只有class A能被外邊的包訪問到,可以充當這個包對外的“介面” (註:不是java 的interface ), B,C只是包級可見的類, 相當於包內部的實現了,外界是無法new 出來, 防止了被外界誤用。
只要保證A不發生變化,就不會影響外界使用, B和C想怎麼改就怎麼改!
大臣甲小心地問道:“如果我只想把foo.core中的class B暴露給foo.cmd訪問,同時阻止別的包訪問class B,該怎麼辦呢? ”
“怎麼會有這麼‘變態’的需求? ” 朝中各位大臣都表示不可思議。
國王沉吟道:“程式員的要求是無窮無盡的,例外總是會發生的, 這種需求是存在的, 容朕想想。”
熟悉C++的大臣乙趕緊上奏:“陛下,C++ 有個什麼friend class的概念。 例如在class Node 中宣告了 friend class LinkedList , 那LinkedList 就可以訪問Node 類的屬性和方法了。 ”
大臣甲強烈反對這種做法:“不好不好,雖然看起來給程式員提供了方便,但是給封裝性撕開了一個大口子,如果被濫用,後果不堪設想。”
國王表示同意:“對,還是放棄這種想法吧,保持簡單性最重要。 如果他實在想訪問class B,可以採用兩種辦法:(1) 把 class B 變成 public (2) 透過介面class A來進行代理。”
鬥轉星移,轉眼間Java國王已經傳到了第9世。
這一天,鄰國的Python, JavaScript派使者來訪,受到了國王的熱情招待,席間談到了java package的存在的問題。
Java package的方式雖然不錯,可是還是有很大的弊端,其中最大的弊端就是很多包中的類都是public的, 這就造成了這樣一種情況。
本來是想讓org.foo.api對外提供介面,讓Client去呼叫的,但實際上,只要foo.jar放到classpath中,另外兩個package , org.foo.impl, org.foo.core中的類也就暴漏了。
JavaScript使者說道:“奧, 我原來以為貴國的一個jar檔案就是一個可復用的模組, 現在看來還是遠遠不夠啊!”
“怪不得大家都說,你的一個jar檔案就是class的壓縮包, classpath就是把這些類給平鋪而已。” Python使者笑道。
Java國王心中有點生氣,但是臉上沒有表露出來:“貴國是怎麼實現的啊?”
Python使者想了想, 自家的module好像也差不多,並且只能靠約定(給變數和方法的前面新增下劃線)的方式來實現private , 由於是約定的,外界依然可以訪問。
JavaScript想到自己連module, package都沒有,趕緊噤聲。
Java 國王說:“簡單的jar檔案缺乏一個重要的特性:隱藏內部實現, 寡人打算做一個重要的改變,定義真正的模組!”
“看到沒有? 我打算用一個檔案module-info.java來定義一個模組中有哪些包是可以被export的, 只有那些export的包才能被Client所呼叫, 其他的包對Client都是不可見的。 ”
看到這個設計方案,各個大臣都覺得不錯。 有了模組, 就真正地定義了對外可以訪問的介面,除了介面的那個package之外,其他的package是不可訪問的, 徹底實現了封裝。
Python使者盯著這個圖看了一會兒,說道:“不對吧,假設有這樣的程式碼:”
FooService service = new FooServiceImpl();
“其中FooService 是org.foo.api包下麵的類, FooServiceImpl是org.foo.impl下麵的類, 按照你模組化的要求,這個FooServiceImpl是不能被Client所訪問的, 那怎麼才能建立FooService呢?”
Java國王心想這Python使者對我Java語言挺熟悉的啊,搞得我下不來臺。
“陛下,臣以為可以用工廠樣式解決!” 終於有大臣前來救駕。“建立一個新的類FooServiceFactory,把它放到org.foo.api包下,可以公開呼叫,這樣不就行了?”
public class FooServiceFactory{
public static FooService getFooService(){
return new FooServiceImpl();
}
}
Python使者卻繼續施壓: “不過讓人不爽的是, 這個FooServiceFactory雖然屬於api包,但是需要知道impl包的具體實現。 如果想新增一個FooService的實現,還得修改它。還是不妥啊!”
突然,Java國王拍了一下腦袋,對了,我怎麼把ServiceLoader給忘記了呢?
我可以把原來的模組分成兩個模組,org.foo.api表示介面, org.foo.provider表示實現。
在org.foo.provider中特別宣告,本模組可以提供FooService的實現!
provides org.foo.api.FooService with org.foo.provider.FooServiceImpl
Python使者還是不太明白: “那客戶端怎麼使用呢?”
“簡單,Client 程式碼可以這麼寫:”
Iterable iter =
ServiceLoader.load(FooService.class);
遍歷iter,獲取FooService並且使用。
這樣在執行時,Client的程式碼就可以使用ServiceLoader就可以找到這些具體的實現了, 當然實現可能不僅僅只有一個,Client選擇一個就可以了。
當然,JDK必須得實現這個ServiceLoader,去獲取這些具體的實現。
“這個方案,即不破環封裝性,又提供了足夠的靈活性,相當於在執行時裝配物件,陛下聖明!” 大臣們紛紛拍馬屁。
Python使者見狀,也就不再發言,開始低頭喝酒。
JavaScript 使者半天都沒有開口了,他心裡一直在琢磨,我是不是有點落伍了? Python有模組,Ruby也有模組,這Java的模組化更是搞得如火如荼。模組化極大地提升了封裝性,如果想進行大型專案的開發這模組化是少不了的, 想想自家那凌亂不堪的js檔案, 是時候做出改變了……
(完)
●編號779,輸入編號直達本文
●輸入m獲取文章目錄
演演算法與資料結構
更多推薦《18個技術類公眾微信》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。