Java设计模式(五)

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Java设计模式(五)

--------------代理模式

前言

这里先举几个经典的例子:

1.       Windows快捷方式

Windows快捷方式可以在使一个对象出现在系统的任何磁盘,而不修改原对象,对快捷方式调用与对原对象调用方式一致,这样快捷方式就形成了一个代理角色。

2.我们去中关村买电脑,一般我们都会找代理商去购买,很少有直接去找电脑生产厂家的(也因为不知道去哪里找,DELL的除外),一般在代理商那里我们不但能买到电脑还能得到不少优惠,这样一个过程就形成了一个代理模式。

在我们的面向对象的程序设计中,也存在着代理商这样的角色。下面就跟着这篇文章来看看代理模式的世界吧。。。。。。

代理模式

为什么要使用代理?

授权机制

不同级别的用户对同一对象拥有不同的访问权利,Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.

不能直接操作到某个对象,但又必须和那个对象有所互动

举例两个具体情况:
  (1)
如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.


  (2)
如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.

 

总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.

角色组成

我们还延续电脑代理商的例子阐述一下,这个代理模式中角色的划分

我们先提一下我们要实现的代理模式的需求:我们要求代理商分为3个等级,提供的服务也不尽相同,但都包括销售电脑这一项,而电脑生产商只有一个,只负责把电脑交给代理商负责销售。

抽象主题角色

声明了真实主题和代理主题的共同接口,也就是主要提供的服务。

下面代码就是声明了一个电脑销售的服务

 

public interface AbstractSale {

 

       public void saleComputer();

 

}

 

代理主题角色

内部包含对真实主题的引用,并且提供与真实主体角色相同的接口。

很明显根据需求,代理商要区分等级,不同等级提供不同服务,那么在代理主体角色一定会增加权限判断服务扩展(这两方面都是代理模式的重要应用)。

 

public class ComputerProxy implements AbstractSale {

 

       private ComputerProductor cp = ComputerProductor.getInstance();

 

       private int level;

 

       public ComputerProxy(int level) {

              this.level = level;

       }

 

       @Override

       public void saleComputer() {

              // TODO Auto-generated method stub

              if (this.level >= Constants.SECOND_LEVEL) {

                     this.preSale();

              }

              cp.saleComputer();

              if (this.level == Constants.ONE_LEVEL) {

                     this.afterService();

              }

       }

 

       private void preSale() {

              System.out.println("卖场 打折优惠活动!");

       }

 

       private void afterService() {

              System.out.println("代理商售后跟踪!");

       }

}

 

真实主题角色

定义真是的对象,根据需求电脑生产商只有一个,所有代理源头都是同一生产商,所以我们使用单例模式来实现。

 

public class ComputerProductor implements AbstractSale {

 

       private static ComputerProductor instance = null;

 

       private ComputerProductor() {

 

       }

 

       public static synchronized ComputerProductor getInstance() {

              if (instance == null) {

                     instance = new ComputerProductor();

              }

              return instance;

 

       }

 

       @Override

       public void saleComputer() {

              // TODO Auto-generated method stub

              System.out.println("一台电脑卖出!");

       }

 

}

 

 

测试类

public class MyTest {

       public static void main(String[] args) {

              // TODO Auto-generated method stub

              AbstractSale sale = new ComputerProxy(Constants.ONE_LEVEL);

              sale.saleComputer();

              AbstractSale sale2 = new ComputerProxy(Constants.SECOND_LEVEL);

              sale2.saleComputer();

       }

}

常量设置类

public interface Constants {

       public static final int ONE_LEVEL=3;

       public static final int SECOND_LEVEL=2;

       public static final int LOW_LEVEL=1;

}

 

动态代理模式

自从JDK1.3以后,JAVA语言通过java.lang.reflect库中,提供了三个类来直接支持代理模式。分别是:ProxyInvocoationHandlerMethod。那么我们首先就先来了解一下这3个类或接口。

JDK支持动态代理的类和接口

Proxy    

提供用于创建动态代理类

重要方法:

newProxyInstance(ClassLoader classloader, class<?>[] interfaces

,InvocationHandler h);(整个代理类产生的入口)

参数表:

classloader:   真实主题角色的类加载器

 interfaces:   代理类需要实现的接口列表

          h:      代理主题角色的代理处理程序

InvocationHandler

关联的调用处理程序类。

重要方法:

public Object invoke(Object proxy, Method method, Object[] arg)

        throws Throwable

参数表:

Object proxy    :生成的代理类

Method method :包含当前代理的方法信息类

Object[] arg    :代理方法的参数表数组

 

返回值:Object  代理方法的返回值,我们可以利用

object x= method.invoke(Object obj, Object...args);得到

Method

提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。在动态代理中一般都是抽象代理角色中定义的接口方法。

重要方法:

      public Object invoke(Object obj, Object...args)

             throws IllegalAccessException ,IllegalArgumentException,

               InvocationTargetException

参数表:

Object obj:    当前代理的真实代理类对象

Object...args: 代理方法的参数表

 

动态代理角色及实现

我们还延续上面电脑代理商的例子来定义我们具体的角色,我设想这样一个场景:我去组装新电脑的同时还想带一个CPU给我的“老爷车” 升级,那么按照原来的思路我们就要找两个代理商分别购置上面产品,那么我能不能找一个神通广大的全能代理商呢,我想要什么产品他都能给我代理到,这就是我们现在要介绍的动态代理了。

抽象主题角色

我们定义两个抽象主题角色,分别是购买电脑和购买CPU

 

public interface AbstractSale {

       public String saleComputer(String bland);

}

public interface AbstractCPUSale {

       public void saleCPU();

}

 

真实主题角色

两个厂商分别买整机和CPU

public class ComputerProductor implements AbstractSale {

       private static ComputerProductor instance = null;

       private ComputerProductor() {

       }

       public static synchronized ComputerProductor getInstance() {

              if (instance == null) {

                     instance = new ComputerProductor();

              }

              return instance;

       }

       @Override

       public String saleComputer(String bland) {

              // TODO Auto-generated method stub

              System.out.println("一台电脑卖出!");

              return bland;

       }

}

 

public class InterProducter implements AbstractCPUSale {

       @Override

       public void saleCPU() {

              // TODO Auto-generated method stub

              System.out.println("卖出一个盒装INTER-CPU");

       }

}

代理主题角色处理程序

定义具体完成代理工作的程序,我们可以在其中进行功能扩展,或根据不同的代理对象,做出不同的代理响应。

 

 

/**

 * 完成动态对象代理过程

 * @author Benson

 */

public class ProxyHandler implements InvocationHandler {

       private Object real;   // 传入的真实代理类

       public ProxyHandler(Object real) {

              this.real = real;

       }

       @Override

       /*

        * arg0 代理类 (Proxy) arg1 代理的方法 arg2 代理方法的参数

        */

       public Object invoke(Object arg0, Method method, Object[] arg2)

                     throws Throwable {

              if (this.real instanceof ComputerProductor) {

                     String type = (String) arg2[0];

                     String x = "";

                     if (type.equals("联想") || type.equals("Dell")) {

                            if (type.equals("联想")) {

                                   this.preSale();

                                   this.afterService();

                            } else if (type.equals("Dell")) {

                                   this.afterService();

                            }

                            x = (String) method.invoke(real, type);

                     } else {

                            System.out.println("我们没有代理该品牌电脑!");

                     }

                     return x;

              } else if (this.real instanceof InterProducter) {

                     method.invoke(real, arg2);

              }

              return null;

       }

       // 扩展方法

       private void preSale() {

              System.out.println("卖场 打折优惠活动!");

       }

       private void afterService() {

              System.out.println("代理商售后跟踪!");

       }

}

 

代理主题角色

这里的代理主题角色是由Proxy类为我们自动生成的,当然我们也可定义一个该角色,负责封装这个 生成过程,在实际操作中由个人习惯而定。

我们在这里就把客户端调用类和代理主题的生成合并处理了

 

public class MyTest {

       /**

        * @param args

        */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

       ComputerProductor cp = ComputerProductor.getInstance(); // 厂家(需要代理的类 )

              ProxyHandler handler = new ProxyHandler(cp);

 

              AbstractSale as = (AbstractSale) Proxy.newProxyInstance(

                            ComputerProductor.class.getClassLoader(),

                            ComputerProductor.class.getInterfaces(), handler);

              String bland = as.saleComputer("联想");

              System.out.println("此产品 品牌是:" + bland);

 

              InterProducter ip=new InterProducter();

              handler = new ProxyHandler(ip);

              AbstractCPUSale ac = (AbstractCPUSale) Proxy.newProxyInstance(

                            InterProducter.class.getClassLoader(), InterProducter.class

                                          .getInterfaces(), handler);

              ac.saleCPU();

       }

}

 

通过这种方式我们就在运行根据真实主题角色动态生成代理类,从而实现了非常灵活的动态代理关系。

与普通代理的区别

动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反 射功能动态生成的。和java.lang.reflect.InvocationHandler结合,可以加强现有类的方法实现。

总结

代理模式从宏观上理解就是:一个人或一个对象(代理主体角色)代替另一个人或者一个对象(真实主体角色)完成某项任务(抽象主体角色中定义的接口)。

 

代理模式跟适配器模式的区别:适配器改变了接口,代理没有。代理是为了对接口进行一个封装并且实现存取控制。适配器是为了适配原有接口,实现代码复用(目的不是控制);

 

所谓动态代理是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个动态代理其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值