代理的本質無論任何時候,只要談到設計模式,大腦中一定要蹦出這四個字“活學活用”。要想對某個事物做到活學活用,必須要對它足夠瞭解,甚至要剖析到本質才行。總是會有些人說,我幹嘛要知道原理,幹嘛要去看源碼?會用就行了。對於這種情況,我只有五個字相送,“你開心就好”。不可否認,認識一個陌生事物,大部分情況還 ...
代理的本質
無論任何時候,只要談到設計模式,大腦中一定要蹦出這四個字“活學活用”。
要想對某個事物做到活學活用,必須要對它足夠瞭解,甚至要剖析到本質才行。
總是會有些人說,我幹嘛要知道原理,幹嘛要去看源碼?會用就行了。對於這種情況,我只有五個字相送,“你開心就好”。
不可否認,認識一個陌生事物,大部分情況還是要從定義開始。
代理模式:為某對象提供一種代理以控制對該對象的訪問,從而限制、增強或修改該對象的一些特性。
如果對代理模式本身就很熟悉的人,一眼就明白什麼意思,甚至連代碼怎麼寫都會浮現在腦海中。
關鍵是對代理模式一點都不熟悉的人,看到定義後絕對一臉懵。下麵通過簡單的圖形來揭開迷惑。
沒有使用代理模式,如下圖01:
“源”直接訪問“目標”。
使用了代理模式,如下圖02:
“源”訪問代理,代理訪問“目標”。
人群中一定會有兩種聲音:
1)設計模式是很高深莫測的東西,有這麼簡單嗎?
2)這怎麼沒有代碼實現啊?
來聽聽作者的看法:
1)高深莫測和簡單不一定都是對沖的。幸福絕對是高深莫測的,那什麼是幸福呢,最多的答案恐怕就是,“一畝土地兩頭牛,老婆孩子熱炕頭”。
多麼簡單朴實的答案,可惜,包括我在內的很多人都追求不到。主要原因是我們人為(主觀)的把很多事情搞複雜了。下麵這個事情可以說明這一點。
中秋節放假時到附近的一個小景區去看一看,我發現有一種果樹的果子掛滿枝頭,又大又圓,讓人看了之後特別有欲望,甚至垂涎欲滴,可惜沒有一個人去摘。
我的第一反應就是果子肯定不能吃。過了一會兒,終於抑制不住好奇心,就找了一個比較矮的果樹,摸了摸枝頭上的果子。果然硬如磐石。
2)對於一上來就說代碼實現的人,只能認為是你擁有了一把錘子之後,看什麼都像釘子。
代碼實現永遠都是最後一步,但在它之前,要找出問題,分析問題,給出方案,論證方案可行性。如果這些都OK了,代碼就是水到渠成的事了。
記住這句話:
認清原理,搞清本質,永遠都是最重要的,不單單是在寫代碼上,在社會上依然如此。
那代理的本質是什麼呢?就兩個字,“加層”,即增加一(多)層。這就是本質。
像其它的什麼“控制訪問”啦,“增強或修改特性”啦,只不過是這個“層"產生的一些(副)作用罷了。
生活中充滿著代理
雖然大部分人都沒有參與過訴訟,但作為常識我們都知道,當事人可以通過協議把自己的一些權力授權給律師,律師就可以在法庭上行使這些權利,此時律師就是代理。
現在網購已成為生活的一部分,但是收快遞卻比較麻煩,因為通常家中無人。此時菜鳥驛站(或媽媽驛站)出現了,它幫我們簽收和暫存包裹,所以它就是代理。
現在生活壓力大,每個人都要上班,所以中午和下午都沒有時間去接送自己上小學的孩子,此時只能選擇午托,我們交了托費之後就等於給了它授權,它代替我們去接送孩子,可見午托也是代理。
還有各種產品的代理商,有大區代理,省級代理,市級代理,等等。還有就是微商/代購,微商自己沒有貨品,只是發發朋友圈,最終是別人發貨。代購就更直接了,代替你去購買,然後再郵寄給你。
明星藝人都有自己的經紀人,可以替自己接一些活動,討價還價,安排日程等。此時經紀人就是代理。還有大BOSS也會請一到多個秘書,來代替自己做一些事情。
可見,生活中充滿著很多代理,他們以各種各樣的形式存在著,發揮著各種各樣的功能和作用,也確實解決了很多社會和生活的問題。
但從本質來看,代理大都以“層”的形式呈現,站在老闆的前面,替老闆做事情。
和電腦相關的代理
大部分人可能都聽過這樣一句話,凡是遇到不好解決的問題,大都可以通過加一層得到有效的解決。加的這個層很多時候和代理有關。
為了網路安全問題,可以加一層防火牆,防火牆雖然不完全是代理,但卻用到許多代理的理論和技術。
為了擴充本地區域網絡,可以加一層交換機或路由器,它用來代理和轉髮網絡請求。還會有一些附帶的其它功能。
為了平衡多個伺服器的處理能力,我們在前面加一個請求路由層,也就是負載均衡器了,如Nginx,它可以代理請求,並按規則轉發。
再說說CRUD,原來是我們寫代碼直接使用JDBC訪問資料庫,現在我們寫代碼使用的是ORM框架,ORM框架再使用JDBC去訪問資料庫。ORM框架可以看作是JDBC的代理。
我們可以看到,從硬體到中間件,再到程式框架,都有代理的影子。
和編程相關的代理
上面所說的代理,都是廣義的代理,主要側重於角色和功能。
一旦在編程中談到代理,基本就是狹義代理了。除了廣義代理的要求外,還要保持類型的相容和“介面”的一致。
說白了就是需要被代理對象的地方,給它一個代理也可以。可以在被代理對象上調用的方法,在代理上也可以調用。
這個要求和代理模式中的要求是一樣的。
如果被代理的對象是一個類。我們用Target表示。
class Target {
public String getDateTime() {
return "2019-10-09"
}
}
為了保持類型相容和介面一致,我們需要生成一個子類。
除此之外還要有被代理的對象,所以還需一個成員變數。
為了添加一些功能,通常需要重寫一些方法。
class Proxy extends Target {
private Target target;
@Override
public String getDateTime() {
return target.getDateTime() + ", 星期三";
}
}
這樣就生成了一個代理,在需要Target的地方換成Proxy也沒有問題,而且還會在日期後面加上星期。
如果被代理的對象是一個介面。我們用ITarget表示。
interface ITarget {
String sayHello(String name);
}
class Target implements ITarget {
@Override
public String sayHello(String name) {
return "hello " + name;
}
}
此時我們只需實現介面即可(當然也可以生成子類),其它的保持不變。
class Proxy implements ITarget {
private ITarget target;
@Override
public String sayHello(String name) {
return target.sayHello(name) + ", long time no see.";
}
}
這也是一個代理,同樣可以使用Proxy代替Target,而且在原來問好的基礎上增加了更多的話語。
由於類型相容且介面一致,所以用戶代碼有時也不知道到底是對象本身還是它的代理,不過這通常並不重要。
代理的好處我們已經看到了,但是也有不好的地方,就是要寫代理的代碼,造成代碼量增加。
這個問題已經通過動態代理解決了。在Java里比較有名的動態代理,就是JDK動態代理和CGLIB代理。這大家都知道了。
全文總結:
代理的本質就是通過加一層來解決問題。類型相容和介面一致只是限制條件而已。
代理有著廣泛的應用,想想Spring的成功,代理貢獻了多少,絕對功不可沒。
仔細體會下“加層”的含義,在代碼中和生活中,你會發現它真的很牛掰格拉斯。