本文主要是看了《設(shè)計(jì)模式》做的筆記和思考蛆封,在此分享僅代表個(gè)人觀點(diǎn)杀糯,如有不對(duì)的地方歡迎批評(píng)和指正。
基礎(chǔ)
Proxy模式彤枢,中文名“代理模式”狰晚,該模式的主要想法是用來(lái)管理被代理的類(lèi),在訪問(wèn)真正的類(lèi)之前缴啡,你只能訪問(wèn)到一個(gè)代理類(lèi)壁晒,代理類(lèi)會(huì)根據(jù)需要合理地支配被代理類(lèi),UML如下所示业栅。
此處秒咐,代理類(lèi)和被代理類(lèi)都應(yīng)當(dāng)實(shí)現(xiàn)同樣的接口,以方便實(shí)現(xiàn)一致性碘裕,使得代理類(lèi)和被代理類(lèi)的使用過(guò)程相同携取。
代理模式的四種使用情況
- 遠(yuǎn)程代理
- 虛代理
- 保護(hù)代理
- 智能指引
下面將逐個(gè)講講其概念以及給出簡(jiǎn)單的代碼作為演示。
遠(yuǎn)程代理
Remote Proxy可以隱藏一個(gè)對(duì)象存在與不同地址空間的事實(shí)帮孔。
為了達(dá)到Remote Proxy的描述雷滋,我的理解有以下兩種情況:
- Proxy類(lèi)中有多個(gè)RealSubject的指針,這些不同的RealSubject共同完成了一個(gè)功能文兢,而Proxy類(lèi)則向外提供了一個(gè)整體的入口晤斩,有點(diǎn)類(lèi)似Facade(外觀)模式,代碼示例如下:
class A extends Subject{}
class B extends Subject{}
class Proxy extends Subject{
private A a;
private B b;
public doSomething(){
a.doSomething();
b.doSomething();
}
}
- Proxy類(lèi)充當(dāng)一個(gè)負(fù)載均衡器的作用姆坚,在內(nèi)存的不同地方有多個(gè)RealSubject澳泵,Proxy類(lèi)根據(jù)實(shí)際情況提供某個(gè)地址的指針
class RealSubject extends Subject{}
class Proxy extends Subject{
private ArrayList<RealSubject> list;
private static int counter = 0;
public doSomething(){
list.get(counter%list.size()).doSomething();
counter++;
}
}
虛代理
如果RealSubject在執(zhí)行某種操作時(shí)開(kāi)銷(xiāo)很大,可以考慮使用Proxy類(lèi)緩存這些操作的結(jié)果兼呵,當(dāng)操作的輸入不變時(shí)兔辅,直接拿Proxy類(lèi)緩存的結(jié)果返回,節(jié)省開(kāi)銷(xiāo)击喂。又或者是RealSubject本身在創(chuàng)建時(shí)就消耗很多資源维苔,但有些功能并不需要完整的RealSubject類(lèi)執(zhí)行,那么這些小功能就可以由Proxy類(lèi)代勞茫负,到實(shí)在需要的時(shí)候再實(shí)例化RealSubject蕉鸳。示例代碼如下所示乎赴。
abstract class Image{
public double getSize();
public String getName();
public void draw();
}
class RealImage extends Image{
private String name;
private int status;
// 構(gòu)造方法忍法,注意它與Proxy中的不同
public RealImage(String name){
this.name = name;
...
}
public double getSize(){、
// 經(jīng)過(guò)某些特殊處理...
}
public String getName(){...}
public void draw(){
// big project ...
}
// 返回一個(gè)狀態(tài)標(biāo)識(shí)
public int getStatusFlag(){...}
}
class ImageProxy extends Image{
private RealImage image;
private int imageStatus = 0; //表示初始狀態(tài)
private double imageSize = 0;
private String imageName = "";
public ImageProxy(String name){
imageName = name;
image = "";
}
public double getSize(){
if(image == null){
image = new RealImage();
}
// 如果RealImage的狀態(tài)沒(méi)有改變榕吼,那么直接返回緩存的值
if(imageStatus 饿序!= image.getStatusFlag()){
imageSize = image.getSize();
}
return imageSize;
}
// 這個(gè)方法就不經(jīng)過(guò)RealImage
public String getName(){
return imageName;
}
// 這時(shí)不用RealImage實(shí)在不行了
public void draw(){
if(image == null){
image = new RealImage();
}
image.draw();
}
}
使用時(shí)就直接使用ImageProxy就好。
保護(hù)代理
這個(gè)就是我在外面看的好幾個(gè)博客講的情形羹蚣,大致描述一下:代理類(lèi)會(huì)審核發(fā)給被代理類(lèi)的請(qǐng)求原探。例子跟這個(gè)類(lèi)似:A找B玩,結(jié)果B媽來(lái)見(jiàn)A,B媽說(shuō)B要讀書(shū)學(xué)習(xí)咽弦,在A看來(lái)徒蟆,這跟B自己說(shuō)要學(xué)習(xí)結(jié)果一樣。這個(gè)例子中型型,B媽就是B的代理段审,在A提出玩的請(qǐng)求時(shí)她先把關(guān)一下,符合條件了再把A的請(qǐng)求轉(zhuǎn)發(fā)給B闹蒜。
智能指針
這里的智能主要描述的是內(nèi)存管理寺枉,包括內(nèi)存回收、長(zhǎng)期持有一個(gè)對(duì)象绷落、同步鎖檢測(cè)姥闪。
要做到內(nèi)存回收,那么通常需要記下引用計(jì)數(shù)砌烁,當(dāng)計(jì)數(shù)降到一個(gè)閾值后觸發(fā)內(nèi)存回收筐喳,以下代碼同時(shí)還展示了同步鎖的使用。
abstract class Box{
public void putSomething(Object o);
public void takeSomething(Object o);
}
// 省略被代理類(lèi)RealBox
// 假設(shè)因?yàn)槟撤N原因函喉,RealBox中并沒(méi)有計(jì)數(shù)疏唾,也沒(méi)有同步鎖
class ProxyBox extends Subject{
private int thingsQty = 0;
private RealBox box; // ProxyBox將在構(gòu)造方法中創(chuàng)建
private bool isReleaseBox = false;
public void synchronize putSomething(Object o){
if(isReleaseBox){
System.out.println("這個(gè)盒子已空");
return;
}
thingsQty++;
box.putSomething(o);
}
public void synchronize takeSomething(Object o){
if(isReleaseBox){
System.out.println("這個(gè)盒子已空");
return;
}
if(thingsQty == 0){
System.out.println("這個(gè)盒子已空");
box = null;
isReleaseBox = true;
}else{
box.takeSomething(o);
}
}
}
main(){
Box box = new ProxyBox();
box.putSomething("apple");
box.takeSomething("apple");
}
我覺(jué)得這種計(jì)數(shù)在RealSubject類(lèi)本身其實(shí)就能做,除非語(yǔ)言不支持在類(lèi)中釋放自己函似。當(dāng)然還有像上面例子中的情況可以使用槐脏,就是當(dāng)你的隊(duì)友出現(xiàn)了疏忽,你又不愿意改他的類(lèi)撇寞,那么可以通過(guò)寫(xiě)一個(gè)Proxy類(lèi)繞過(guò)這些坑顿天。此時(shí),這個(gè)Proxy已經(jīng)跟Decorator很像了蔑担,只要去除Proxy的權(quán)限牌废,這個(gè)就是Decorator模式,此處是根據(jù)isReleaseBox設(shè)置權(quán)限啤握,如果讀者有更好的方法歡迎提出鸟缕。
Proxy(代理)模式與Decorator(裝飾)模式
需要說(shuō)明的是,在Decorator只有一層的時(shí)候排抬,這兩個(gè)模式連UML都極為相似懂从,如上文所說(shuō),一旦Proxy類(lèi)放棄對(duì)RealSubject的控制蹲蒲,它很大概率就是個(gè)Decorator類(lèi)番甩。但從設(shè)計(jì)思想來(lái)說(shuō),它們區(qū)別挺大的届搁。Decorator模式主要適用于應(yīng)對(duì)不可預(yù)見(jiàn)的功能(設(shè)計(jì)者甚至不知道用戶會(huì)拿它的類(lèi)嵌套實(shí)現(xiàn)什么功能)缘薛,因此它非常的靈活窍育,也能夠遞歸地、層級(jí)式地完成某些功能宴胧。Proxy模式主要適用于不便于訪問(wèn)RealSubject時(shí)使用漱抓,因而,Proxy類(lèi)與RealSubject的關(guān)系是可以靜態(tài)表達(dá)的恕齐、可預(yù)見(jiàn)的辽旋,也因?yàn)檫@一點(diǎn),它不夠靈活檐迟。因此补胚,推薦從設(shè)計(jì)思想出發(fā),在盡可能地表達(dá)業(yè)務(wù)中各個(gè)角色的關(guān)系的同時(shí)追迟,追求靈活與穩(wěn)定溶其。