設計模式(七)——代理模式

liangxingwei發表於2017-12-14

本文原創掘金:L_Sivan

代理模式

  • 定義:為其他物件提供一種代理以控制對這個物件的訪問(原話是:Provide asurrogate or placeholder foranother object to control access to it. )
  • 寫這篇文章的時候,因為已經把書都看過了,所以有點搞不清代理模式和裝飾器模式的區別。但是現在一看定義就很清楚明瞭了,提供代理,以控制對這個物件的訪問,看到黑體加粗的控制沒,這才是代理模式的精髓。(因為沒有這個控制的話,和裝飾器模式沒太大區別)
  • 屬於行為類模式
  • 分類:靜態代理,動態代理

舉個例子

首先要明確一件事,設計模式的提出一定是為了解決某一些共性的,在編碼上遇到的問題。重點字是控制,所以舉一個要訪問物件,要被另外的東西來控制的例子。比如我很喜歡彭于晏,我很想讓彭于晏幫我籤個名,但是我不能直接找到彭于晏,要找他的經紀人(代理)來幫忙,就來模仿這個吧。沒用到代理模式

// 彭于晏
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("這不是我的經紀人,我不籤");
		}
	}
	// 提供經紀人的獲取途徑,畢竟經紀人也是確定的
	public AgentOfEddie getAgentOfEddie() {
		eddieAgent = new AgentOfEddie(this);
		return eddieAgent;
	}
	public static Eddie getEddie() {
		return eddie;
	}
}
// 彭于晏的經紀人
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();
	}
}
結果:
Eddie:VivoX9柔光雙攝,照亮你的美
// 場景類2,直接new一個經紀人來簽名
public class Cilent {
	public static void main(String[] args) {
		Eddie eddie = Eddie.getEddie();
		AgentOfEddie agentOfEddie = new AgentOfEddie(eddie);
		agentOfEddie.getEddieSign();
	}
}
結果:
這不是我的經紀人,我不籤
// 場景類3,直接讓彭于晏簽名
public class Cilent {
	public static void main(String[] args) {
		Eddie eddie = Eddie.getEddie();
		eddie.sign(new AgentOfEddie(eddie));
	}
}
結果:
這不是我的經紀人,我不籤
複製程式碼

程式碼的意思就是,只有通過Eddie獲取到的經紀人去sign,才可以讓彭于晏來簽名,除此之外,無論是自己new的經紀人去sign還是直接用eddie執行sign,都不能獲取到簽名。 看起來並無毛病,還完美實現,打出三秒的控制效果。確實,寫這個例子的時候我都有點懷疑代理模式是用來幹嘛的這種感覺。對嘛,看書上說,比如在現實生活中,打官司找律師,就是為了避免官司的種種是是非非,只需要做好自己的答辯就行了。 用上面的程式碼來講,就是彭于晏可以不用處理對外的事情,交給經紀人就行了,那也沒毛病啊,上面的程式碼還是可以用的,經紀人那裡加控制不就行了嗎?而且也符合定義提供一種代理以控制對這個物件的訪問。(對的,這裡的重點針對的是,**為什麼代理模式要用介面來實現?這個介面是必要的嗎?**看過代理模式的應該會懂的。我上面就沒用介面,而且也實現了,所以我就沒想明白) 讓我想清楚的,是Spring 的aop程式設計,aop是面向切面程式設計,而這個切面,通俗點來講就是一個個的方法,那麼spring是如何做到可以對每一個方法都實行切面控制的呢(雖說是動態代理)?嘿嘿,是不是有點頭緒了。 回到上面的程式碼,假設彭于晏不止要簽名,還要拍Vivo的廣告,還要拍益達的廣告,還要拍湄公河行動,這些都是要和外面商量的,需要經紀人來控制的,這麼做會造成什麼問題?彭于晏每多一個對外的行動,經紀人都要多一個對外的行動,生活上是這樣的,但放在上面的程式碼,經紀人對著彭于晏的對外行動一個一個新增方法!!!可怕吧。

用代理模式要怎麼做呢?靜態代理例子

// 定義明星介面
public interface Celebrity {
	// 簽名
	public void sign(Celebrity celebrity);
	// 拍廣告
	public void makeAdvertising(Celebrity celebrity);
}
// 實現明星介面的彭于晏
public class Eddie implements Celebrity{
	private Eddie() {	}
	// 單例,叫彭于晏的有很多,拍Vivo廣告的只有一個
	private final static Eddie eddie = new Eddie();
	private AgentOfEddie eddieAgent;
	// 提供經紀人的獲取途徑,畢竟經紀人也是確定的
	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("這不是我的經紀人,我不籤");
		}
	}
	// 拍廣告
	@Override
	public void makeAdvertising(Celebrity celebrity) {
		if (celebrity == this.eddieAgent) {
			System.out.println(this.getClass().getSimpleName() + "在拍廣告:VivoX9柔光雙攝,照亮你的美");
		} else {
			System.out.println("這不是我的經紀人,我不拍");
		}
	}
}
// 實現明星介面的彭于晏的經紀人
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);
	}
}
結果:
Eddie:VivoX9柔光雙攝,照亮你的美
Eddie在拍廣告:VivoX9柔光雙攝,照亮你的美
// 場景類2,直接new一個經紀人來執行簽名這件事
public class Cilent {
	public static void main(String[] args) {
		Eddie eddie = Eddie.getEddie();
		AgentOfEddie agentOfEddie = new AgentOfEddie(eddie);
		agentOfEddie.sign(agentOfEddie);
		agentOfEddie.makeAdvertising(agentOfEddie);
	}
}
結果:
這不是我的經紀人,我不籤
這不是我的經紀人,我不拍
// 場景類3,直接讓彭于晏簽名
public class Cilent {
	public static void main(String[] args) {
		Eddie eddie = Eddie.getEddie();
		eddie.sign(new AgentOfEddie(eddie));
	}
}
結果:
這不是我的經紀人,我不籤
這不是我的經紀人,我不拍
複製程式碼

分析下程式碼,首先規定了一個介面Celebrity,用來規定明星應該具有的行為,接著,Eddie類實現該介面,AgentOfEddie類也實現該介面,同時,在AgentOfEddie中宣告瞭Eddie,介面的方法實現就用Eddie的方法來實現。規定介面方法的一個好處是,確保代理類(AgentOfEddie)的方法和被代理類(Eddie)的主體方法是一致的,場景類也執行無誤。而上面的這種代理模式,具體一點又叫做強制代理(如果有看其他部落格,應該知道還有普通代理等很多具體的代理方法的,還有JDK動態代理,cglib動態代理,這裡就不舉例子了,因為還沒那個實力╮(╯▽╰)╭)。

代理模式最常用的地方,就是實現對另一個物件的控制,比如說我們J2EE的攔截器,就是代理模式活生生的應用,在被攔截物件的方法的執行前後進行事務控制。當然還有很多應用地方的,慢慢探討吧。

水平有限,難免有錯,還請評論區指責下

相關文章