本文属于系列文章《设计模式》,附上文集链接
代理模式
- 定义:为其他对象提供一种代理以控制对这个对象的访问(原话是: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
,接口的方法实现就用<code>Eddie</code>的方法来实现。规定接口方法的一个好处是,确保代理类(AgentOfEddie
)的方法和被代理类(Eddie
)的主体方法是一致的,场景类也执行无误。而上面的这种代理模式,具体一点又叫做强制代理(如果有看其他博客,应该知道还有普通代理等很多具体的代理方法的,还有JDK动态代理,cglib动态代理,这里就不举例子了,因为还没那个实力╮(╯▽╰)╭)。
代理模式最常用的地方,就是实现对另一个对象的控制,比如说我们J2EE的拦截器,就是代理模式活生生的应用,在被拦截对象的方法的执行前后进行事务控制。当然还有很多应用地方的,慢慢探讨吧。
网友评论