代理模式——公司的代言人

静态代理

  随着业务规模的增大,为了方便管理两间工厂,小成和他的合伙人建立了一间公司,把一些不是很重要的生意交给手下业务员代表公司去和其他公司谈,如果业务员超常发挥,还可能为公司谈好一笔任务之外的生意。这样老板小成就可以轻松很多了,小成一有空就想写代码,一想这个不就是代理模式吗,然后就开始写下代码。

介绍

  在有些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个代理来实现间接引用。就像我们现在的不能直接访问谷歌,要通过代理翻墙才行。

  代理模式的定义就是为其他对象提供一种代理以控制对这个对象的访问。在代理模式中,代理者在代替被代理者执行操作时,还可以增加自己的操作。

  代理模式中有几个重要的角色:

- 抽象对象父类(Subject):是真实对象类和代理类的共同父类,这样一来在任何使用真实对象类的地方都可以使用代理类。

- 真实对象类(RealSubject):定义了代理类所代表的真实对象类,真正执行操作的对象。

- 代理类(Proxy):代理类里面含有对真实对象类的引用,从而可以操作真实对象,而且代理类可以在将客户端调用真实对象的操作之前或之后,加上自己的一些操作,而不是单纯的将调用传递给真实主题对象,像下面的代码,公司代理额外谈好一单生意就是代理自己的操作。

package scut.designmodel.ProxyPattern;

//公司类
abstract class Company {
    //公司名字
    public String CompanyName;
    //公司谈判
    public abstract void Negotiate();
    //公司签约
    public abstract void Sign();
}

//小成的公司,真正的谈判对象,也就是被代理的对象
class XiaoChengCompany extends Company {

    @Override
    public void Negotiate() {
        System.out.println(CompanyName +"正在与其他公司谈判");
    }
    @Override
    public void Sign() {
        System.out.println(CompanyName +"签好了合约");
    }

}

//小成公司的代理
class XiaoChengCompanyProxy extends Company {
    private XiaoChengCompany mXiaoChengCompany;

    //代理建立的构造函数,建立一个公司实例,把公司名信息给它
    public XiaoChengCompanyProxy(String CompanyName){
        mXiaoChengCompany = new XiaoChengCompany();
        mXiaoChengCompany.CompanyName = CompanyName;
    }

    @Override
    public void Negotiate() {
        mXiaoChengCompany.Negotiate();
        ExtraBusiness();
    }

    @Override
    public void Sign() {
        mXiaoChengCompany.Sign();
    }

    //代理的额外动作
    public void ExtraBusiness(){
        System.out.println("代理额外谈好了一单生意");
    }
}

//使用代理和进行其他公司进行谈判签约的过程
public class ProxyPattern {
    public static void main(String[] arg){
        XiaoChengCompanyProxy mXiaoChengCompanyProxy = new XiaoChengCompanyProxy("小成的公司");
        mXiaoChengCompanyProxy.Negotiate();
        mXiaoChengCompanyProxy.Sign();
    }
}

运行结果:

小成的公司正在与其他公司谈判
代理额外谈好了一单生意
小成的公司签好了合约

应用场景

  • 当需要为一个对象再不同的地址空间提供局部的代表时。远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 当需要创建开销非常大的对象时。 虚拟代理可以通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
  • 当需要控制对原始对象的访问时。保护代理可以控制对真实对象的使用权限。
  • 当需要在访问对象时知想一些附加操作时。比如通过代理对象计算访问实际对象的次数。

优点

  1. 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
  2. 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  3. 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
  4. 保护代理可以控制对真实对象的使用权限。

缺点

  1. 静态代理类和委托类实现了相同的接口,静态代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  2. 静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态代理

  小成的公司虽然刚刚成立,但是还是成立了各种部门分管事务。有一些部门很缺人,例如产品部和广告部就需要人手,然后就会委托人事部去招人,人事部就是他们的代理。小成一开始想用静态代理模式来实现这样的代码,但是静态代理对象只服务于一个部门,如果要代理另外一个部门又要重新写,那为什么不写一个可以代理所有部门的代理呢?于是小成就从网上找到了动态代理模式。

介绍

  动态代理模式解决了静态代理的要手动实现代理的缺点,只需要把代理类实现InvocationHandler接口,就可以通过基于java反射机制的method.invoke方法来自动调用被代理类的方法,不用手动实现。

  动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实对象类(RealSubject)相同的接口,而是把这种实现推迟到运行时。

可以看下面的代码:

动态代理类:

  为了能让PersonnelDepartment类能够在运行时才去实现真实对象类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口。

package scut.designmodel.DynamicProxyPattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PersonnelDepartment implements InvocationHandler {

    // 代理对象
    private Object ProxyObject;
    //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。

    public Object newProxyInstance(Object ProxyObject){
        this.ProxyObject =ProxyObject;
        //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
        //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
        //根据传入的目标返回一个代理对象
        return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                ProxyObject.getClass().getInterfaces(),this);
    }
    @Override
    //关联的这个实现类的方法被调用时将被执行
    //InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("人事部准备场地");
        Object result=null;

        switch (method.getName()) {
            case "Negotiate":
            try {
            /*原对象方法调用前代理的动作*/
                System.out.println("人事部邀请应聘者面试谈判");

                //运用java反射机制调用目标方法
                result = method.invoke(ProxyObject, args);

            /*原对象方法调用后代理的动作*/
                System.out.println("谈判成功,人事部制定合同");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("谈判失败");
                throw e;
            }
                break;
            case "Sign":
                try {
            //原对象方法调用前代理的动作
                    System.out.println("人事部邀请应聘者面试签约");

                    //运用java反射机制调用目标方法
                    result = method.invoke(ProxyObject, args);

            //原对象方法调用后代理的动作
                    System.out.println("签约成功,人事部安排新同事上班");
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("签约失败");
                    throw e;
                }
                break;
        }
        System.out.println("----------------");
        return result;
    }

}

实际类:

package scut.designmodel.DynamicProxyPattern;

//部门类
interface Department {

    //部门谈判
    public  void Negotiate();
    //部门签约
    public  void Sign();
}

//广告部门,真正的谈判对象,也就是被代理的对象
class AdvertisingDepartment implements Department {

    @Override
    public void Negotiate() {
        System.out.println("广告部正在和应聘者谈判");
    }
    @Override
    public void Sign() {
        System.out.println("广告部和应聘者签好了合约");
    }

}

//产品部门,真正的谈判对象,也就是被代理的对象
class ProductDepartment implements Department {

    @Override
    public void Negotiate() {
        System.out.println("产品部正在和应聘者谈判");
    }
    @Override
    public void Sign() {
        System.out.println("产品部和应聘者签好了合约");
    }

}

//人事部动态代理两个部门的招聘工作流程
public class DynamicProxyPattern {
    public static void main(String[] arg){

        PersonnelDepartment personnelDepartment = new PersonnelDepartment();
        Department mAdvertisingDepartment = (Department) personnelDepartment.newProxyInstance(new AdvertisingDepartment());
        mAdvertisingDepartment.Negotiate();
        mAdvertisingDepartment.Sign();

        Department mProductDepartment = (Department) personnelDepartment.newProxyInstance(new ProductDepartment());
        mProductDepartment.Negotiate();
        mProductDepartment.Sign();
    }
}

结果:

人事部准备场地
人事部邀请应聘者面试谈判
广告部正在和应聘者谈判
谈判成功,人事部制定合同
1.
----------------
人事部准备场地
人事部邀请应聘者面试签约
广告部和应聘者签好了合约
签约成功,人事部安排新同事上班
----------------
人事部准备场地
人事部邀请应聘者面试谈判
产品部正在和应聘者谈判
谈判成功,人事部制定合同
----------------
人事部准备场地
人事部邀请应聘者面试签约
产品部和应聘者签好了合约
签约成功,人事部安排新同事上班
----------------

应用场合

  动态代理模式的应用场景其实和静态代理的差不多,只是如果静态代理要代理的对象比较多的时候,就建议使用动态代理,可以减少很多重复的代码。

优点

  1. 只需要一个动态代理类就可以解决创建多个静态代理的麻烦,避免不断的重复多余的代码。
  2. 调用目标代码时,会在方法“运行时”才动态调用真实类对象,不需要事先实例化,更加灵活多变。

缺点

  1. 相比静态代理的直接调用被代理类对象,动态代理要通过反射,这样的话效率降低了一点。
  2. JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,不过可以通过cglib来实现针对类的代理。

参考

  1. http://blog.csdn.net/hejingyuan6/article/details/36203505#t2
  2. 《设计模式其实很简单》,刘径舟,张玉华等编著——清华大学出版社,2013.7
时间: 2024-10-05 09:42:02

代理模式——公司的代言人的相关文章

从公司买火车票到代理模式和适配器模式

近期看到一些设计模式的文章,自己也有了些想法. 现公司是个爱出差的公司,不可避免要买许多火车票.这个买票的流程一直在变化. 最开始大家都是自己买火车票,然后回公司的时候报销. 就像这样: public class AtFirst { public static void main(String[] args) { EmployeeABuy. Abuy(); //报销 EmployeeBBuy. Bbuy(); //报销 } } class EmployeeABuy{ public static

深刻理解代理模式在java中如何优化系统性能

最近参与实习公司的项目迭代,项目中需要实现系统的快速响应和大量数据处理.在不断的学习中获得一点儿心得,先记录下来.慢慢的修改! 关于代理模式的知识和简单应用之前的学习笔记中已经有记录了,可以复习一下.这里主要记录如何使用代理模式来实现延迟加载,进而提升系统系能和反应速度. 使用代理模式实现延迟加载的一个简单实例: 需求:项目中对系统的启动速度做了一定的要求 我们在系统首次加载时,因为不需要实际的数据来构造显示界面,这就为我们实现系统首次加载的快速响应提供了可能.在常规模式下,我们一般会在系统启动

代理模式的概念

代理模式:就是在不修改目标对象的基础上使用代理对象增加目标对象功能的. 根据代理对象与目标对象之间的 关系,代理关系产生的时机可以分为二类:静态代理与动态代理 静态代理:在程序运行前,代理类与目标类之间的代理关系就已经确立了,从代码上看:有代理类的才是静态代理,比如公司与法律顾问 动态代理:程序运行时才产生代理关系,从代码上看:没有代理类,只有代理对象的才是动态代理(代理对象是有代理工厂或者代理工具生成的),比如个人与律师

4 结构型模式之 - 代理模式

代理模式的介绍:代理模式也称为委托模式,在开发中经常用到,是编程的好帮手,在日常生活中也比较常见.比如公司中午让同事帮忙带一份饭,比如我们请一个律师打官司,比如我们用代理服务器上网等等.代理模式真是无处不在. 代理模式的定义:为其它对象提供一种代理以控制对这个对象的访问. 代理模式的使用场景:当无法或者不想直接访问某个对象或者访问某个对象存在困难时,可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象或者代理对象需要实现相同的接口. 代理模式一般的写法(静态代理模式): 代理类和

研磨设计模式之——代理模式(一)

案例: 在一个项目中客户提出,选择一个部门或者分公司时,要把这个部门或者分公司的所有员工显示出来,而不要翻页.在现实员工时,只需要显示 名称即可,但是也需要提供查看员工详细信息. 不用模式的解决方案 用sql语句查询改分公司下的所有员工信息.我们使用jdbc模拟,底层数据库为mysql sql脚本如下: 1 SET FOREIGN_KEY_CHECKS=0; 2 3 -- ---------------------------- 4 -- Table structure for tbl_dep

设计模式学习笔记(十九:代理模式)

1.1概述 为其他对象提供一种代理以控制对这个对象的访问.这就是代理模式的定义. 当用户希望和某个对象打交道,但程序可能不希望用户直接访问该对象,而是提供一个特殊的对象,这个特殊的对象被称作当前用户要访问对象的代理,程序让用户和对象的代理打交道,即让用户通过访问代理来访问想要访问的对象.在代理模式中,代理的特点是:它与所代理的对象实现了相同的接口,也就是说代理和它多代理的对象向用户公开了相同的方法,当用户请求代理调用该方法时,代理可能需要验证某些信息或检查它所代理的对象是否可用,当代理确认它所代

Java设计模式:代理模式(一)

问题的提出 现在生活中,常常在微信朋友圈里面看到代购的信息,你想在国外买什么,香港买什么,但是又懒得自己过去,于是常常委托别人帮忙买奶粉买那啥的.这类问题的缘由是因为客户和原产地没有直接的接触,所以需要一个代理(代购)的第三者来实现间接引用.代理对象可以在客户端和目标对象间起到中介作用,而且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务.代理模式是一种很好实现客户对象与代理对象分离的策略.其抽象UML图如下图 代理模式包含如下角色 ISubject:抽象主题角色,是一个接

JAVA设计模式:代理模式

最近公司从新开发一个项目,为此对老的代码进行评估分析发现有些地方是可以采用代理模式来进行的,为此对代理模式进行了一次理解: 代理模式:即为指定的目标对象提供一个代理商,由代理商来完成对目标对象的操作. 代理类图: 抽象对象角色或者接口角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方就都可以使用代理对象. 目标对象角色:定义了所需要代理对象的真实对象. 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象:代理对象通常在客户端调用目标对象之前或

Java中的代理模式

1.什么是代理模式 代理模式:就是为其他对象提供一种代理以控制对这个对象的访问. 代理可以在不改动目标对象的基础上,增加其他额外的功能(扩展功能). 举个例子来说明代理的作用: 一般我们想邀请明星来当我们的代言人,我们并不能直接联系到明星,而是通过其经纪人,来告诉经纪人我们需要和明星进行合作,然后通过经纪人来转达给明星.,明星只需要做好代言工作就好,其他繁琐的事情就交于经纪人就可以.这里的经经纪人就是一个代理对象,明星就是一个目标对象. 用图表示如下: 2.三种代理模式  2.1 静态代理 静态