本文屬于系列文章《設(shè)計模式》,附上文集鏈接
代理模式
- 定義:為其他對象提供一種代理以控制對這個對象的訪問(原話是:Provide asurrogate or placeholder foranother object to control access to it. )
- 寫這篇文章的時候,因為已經(jīng)把書都看過了,所以有點搞不清代理模式和裝飾器模式的區(qū)別。但是現(xiàn)在一看定義就很清楚明了了,提供代理,以控制對這個對象的訪問,看到黑體加粗的控制沒,這才是代理模式的精髓。(因為沒有這個控制的話,和裝飾器模式?jīng)]太大區(qū)別)
- 屬于行為類模式
- 分類:靜態(tài)代理,動態(tài)代理
舉個例子
首先要明確一件事,設(shè)計模式的提出一定是為了解決某一些共性的,在編碼上遇到的問題。重點字是控制,所以舉一個要訪問對象,要被另外的東西來控制的例子。比如我很喜歡彭于晏,我很想讓彭于晏幫我簽個名,但是我不能直接找到彭于晏,要找他的經(jīng)紀(jì)人(代理)來幫忙,就來模仿這個吧。沒用到代理模式
// 彭于晏
public class Eddie {
private Eddie() { }
// 單例,叫彭于晏的有很多,拍Vivo廣告的只有一個
private final static Eddie eddie = new Eddie();
private AgentOfEddie eddieAgent;
public void sign(AgentOfEddie eddieAgent) {
if (eddieAgent == this.eddieAgent) {
System.out.println(this.getClass().getSimpleName() + ":VivoX9柔光雙攝,照亮你的美");
} else {
System.out.println("這不是我的經(jīng)紀(jì)人,我不簽");
}
}
// 提供經(jīng)紀(jì)人的獲取途徑,畢竟經(jīng)紀(jì)人也是確定的
public AgentOfEddie getAgentOfEddie() {
eddieAgent = new AgentOfEddie(this);
return eddieAgent;
}
public static Eddie getEddie() {
return eddie;
}
}
// 彭于晏的經(jīng)紀(jì)人
public class AgentOfEddie {
public AgentOfEddie(Eddie eddie){
this.eddie = eddie;
}
private Eddie eddie;
// 讓杰倫哥簽名
public void getEddieSign(){
eddie.sign(this);
}
}
// 場景類1
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
AgentOfEddie agentOfEddie = eddie.getAgentOfEddie();
agentOfEddie.getEddieSign();
}
}
結(jié)果:
Eddie:VivoX9柔光雙攝,照亮你的美
// 場景類2,直接new一個經(jīng)紀(jì)人來簽名
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
AgentOfEddie agentOfEddie = new AgentOfEddie(eddie);
agentOfEddie.getEddieSign();
}
}
結(jié)果:
這不是我的經(jīng)紀(jì)人,我不簽
// 場景類3,直接讓彭于晏簽名
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
eddie.sign(new AgentOfEddie(eddie));
}
}
結(jié)果:
這不是我的經(jīng)紀(jì)人,我不簽
代碼的意思就是,只有通過Eddie獲取到的經(jīng)紀(jì)人去sign,才可以讓彭于晏來簽名,除此之外,無論是自己new的經(jīng)紀(jì)人去sign還是直接用eddie執(zhí)行sign,都不能獲取到簽名。
看起來并無毛病,還完美實現(xiàn),打出三秒的控制效果。確實,寫這個例子的時候我都有點懷疑代理模式是用來干嘛的這種感覺。對嘛,看書上說,比如在現(xiàn)實生活中,打官司找律師,就是為了避免官司的種種是是非非,只需要做好自己的答辯就行了。
用上面的代碼來講,就是彭于晏可以不用處理對外的事情,交給經(jīng)紀(jì)人就行了,那也沒毛病啊,上面的代碼還是可以用的,經(jīng)紀(jì)人那里加控制不就行了嗎?而且也符合定義提供一種代理以控制對這個對象的訪問。(對的,這里的重點針對的是,為什么代理模式要用接口來實現(xiàn)?這個接口是必要的嗎?看過代理模式的應(yīng)該會懂的。我上面就沒用接口,而且也實現(xiàn)了,所以我就沒想明白)
讓我想清楚的,是Spring 的aop編程,aop是面向切面編程,而這個切面,通俗點來講就是一個個的方法,那么spring是如何做到可以對每一個方法都實行切面控制的呢(雖說是動態(tài)代理)?嘿嘿,是不是有點頭緒了。
回到上面的代碼,假設(shè)彭于晏不止要簽名,還要拍Vivo的廣告,還要拍益達的廣告,還要拍湄公河行動,這些都是要和外面商量的,需要經(jīng)紀(jì)人來控制的,這么做會造成什么問題?彭于晏每多一個對外的行動,經(jīng)紀(jì)人都要多一個對外的行動,生活上是這樣的,但放在上面的代碼,經(jīng)紀(jì)人對著彭于晏的對外行動一個一個添加方法!!!可怕吧。
用代理模式要怎么做呢?靜態(tài)代理例子
// 定義明星接口
public interface Celebrity {
// 簽名
public void sign(Celebrity celebrity);
// 拍廣告
public void makeAdvertising(Celebrity celebrity);
}
// 實現(xiàn)明星接口的彭于晏
public class Eddie implements Celebrity{
private Eddie() { }
// 單例,叫彭于晏的有很多,拍Vivo廣告的只有一個
private final static Eddie eddie = new Eddie();
private AgentOfEddie eddieAgent;
// 提供經(jīng)紀(jì)人的獲取途徑,畢竟經(jīng)紀(jì)人也是確定的
public AgentOfEddie getAgentOfEddie() {
eddieAgent = new AgentOfEddie(this);
return eddieAgent;
}
public static Eddie getEddie() {
return eddie;
}
// 簽名
@Override
public void sign(Celebrity celebrity) {
if (celebrity == this.eddieAgent) {
System.out.println(this.getClass().getSimpleName() + ":VivoX9柔光雙攝,照亮你的美");
} else {
System.out.println("這不是我的經(jīng)紀(jì)人,我不簽");
}
}
// 拍廣告
@Override
public void makeAdvertising(Celebrity celebrity) {
if (celebrity == this.eddieAgent) {
System.out.println(this.getClass().getSimpleName() + "在拍廣告:VivoX9柔光雙攝,照亮你的美");
} else {
System.out.println("這不是我的經(jīng)紀(jì)人,我不拍");
}
}
}
// 實現(xiàn)明星接口的彭于晏的經(jīng)紀(jì)人
public class AgentOfEddie implements Celebrity{
public AgentOfEddie(Eddie eddie){
this.eddie = eddie;
}
private Eddie eddie;
// 讓彭于晏簽名
@Override
public void sign(Celebrity celebrity) {
// TODO Auto-generated method stub
eddie.sign(celebrity);
}
@Override
public void makeAdvertising(Celebrity celebrity) {
eddie.makeAdvertising(celebrity);
}
}
// 場景類1
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
AgentOfEddie agentOfEddie = eddie.getAgentOfEddie();
agentOfEddie.sign(agentOfEddie);
agentOfEddie.makeAdvertising(agentOfEddie);
}
}
結(jié)果:
Eddie:VivoX9柔光雙攝,照亮你的美
Eddie在拍廣告:VivoX9柔光雙攝,照亮你的美
// 場景類2,直接new一個經(jīng)紀(jì)人來執(zhí)行簽名這件事
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
AgentOfEddie agentOfEddie = new AgentOfEddie(eddie);
agentOfEddie.sign(agentOfEddie);
agentOfEddie.makeAdvertising(agentOfEddie);
}
}
結(jié)果:
這不是我的經(jīng)紀(jì)人,我不簽
這不是我的經(jīng)紀(jì)人,我不拍
// 場景類3,直接讓彭于晏簽名
public class Cilent {
public static void main(String[] args) {
Eddie eddie = Eddie.getEddie();
eddie.sign(new AgentOfEddie(eddie));
}
}
結(jié)果:
這不是我的經(jīng)紀(jì)人,我不簽
這不是我的經(jīng)紀(jì)人,我不拍
分析下代碼,首先規(guī)定了一個接口Celebrity
,用來規(guī)定明星應(yīng)該具有的行為,接著,Eddie
類實現(xiàn)該接口,AgentOfEddie
類也實現(xiàn)該接口,同時,在AgentOfEddie
中聲明了Eddie
,接口的方法實現(xiàn)就用<code>Eddie</code>的方法來實現(xiàn)。規(guī)定接口方法的一個好處是,確保代理類(AgentOfEddie
)的方法和被代理類(Eddie
)的主體方法是一致的,場景類也執(zhí)行無誤。而上面的這種代理模式,具體一點又叫做強制代理(如果有看其他博客,應(yīng)該知道還有普通代理等很多具體的代理方法的,還有JDK動態(tài)代理,cglib動態(tài)代理,這里就不舉例子了,因為還沒那個實力╮(╯▽╰)╭)。
代理模式最常用的地方,就是實現(xiàn)對另一個對象的控制,比如說我們J2EE的攔截器,就是代理模式活生生的應(yīng)用,在被攔截對象的方法的執(zhí)行前后進行事務(wù)控制。當(dāng)然還有很多應(yīng)用地方的,慢慢探討吧。