设计模式(七)——代理模式

本文属于系列文章《设计模式》,附上文集链接

代理模式

  • 定义:为其他对象提供一种代理以控制对这个对象的访问(原话是: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的拦截器,就是代理模式活生生的应用,在被拦截对象的方法的执行前后进行事务控制。当然还有很多应用地方的,慢慢探讨吧。

水平有限,难免有错,还请评论区指责下

推荐阅读更多精彩内容

  • 代理模式(Proxy Pattern):构建了透明置于两个不同对象之内的一个对象,从而能够截取或代理这两个对象间的...
    刀斧手何在阅读 513评论 1 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 84,085评论 14 122
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    会飞的鱼69阅读 23,460评论 18 391
  • 洛洛突然收到了一条微信,上边写着你不生气了吧?是我不好,我太差劲了,你值得更好的人。千万不要对爱情丧失信心啊。 看...
    杨咩咩YY阅读 195评论 14 1
  • 今天送完女朋友到车上补票发现现金花完了,着急的不行,向旁边几个人微信支付宝转账,他们都已各种理由说不信,列车员也...
    莫名的理解阅读 64评论 0 1