`
senbao18
  • 浏览: 5379 次
  • 性别: Icon_minigender_1
  • 来自: JavaWorld
最近访客 更多访客>>
社区版块
存档分类
最新评论

别装了-第一集:代理模式、动态代理和面向方面

阅读更多
Public class ViewAction implements Action
{
        public void doAction()
        {
               //做View的动作
               System.out.println(“You could view the information……”);
               ……
}
}

代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理。比如过春节要回家,由于你要上班,没时间去买票,就得票务中介代你购买,这就是一种代理模式。这个情景可以形象的描述如下:

class:火车站
{
        卖票:
       {……}
}

 

火车站是卖票的地方,我们假设只能在火车站买到票。卖票的动作实质是火车站类完成的。

Class:票务中介
{
        卖票:
        {
               收中介费;
              火车站.卖票;
}
}

 

 顾客找票务中介买票的时候,调用票务中介.卖票。票务中介其实做了两件事,一是去火车站买票,二是不能白帮你卖票,肯定要收中介费。而你得到的好处是不用直接去火车站买票,节省了买票的时间用来上班。     以上我们简单模拟了代理模式的情景和为什么要使用代理模式,下面我们以一个例子来具体分析一下JAVA中的代理模式。     假设有一个信息管理系统,用些用户有浏览信息的权限,有些用户有浏览、添加和修改信息的权限,还有些用户有除了上述的权限,还有删除信息的权限,那么我们最容易想到的做法如下:
public class ViewAction
{
        //由userId计算权限
        ……
        String permission = ……;
       if(permission.equals(Constants.VIEW))
        {
               System.out.println(“You could view the information……”);
               ……
}
}
  其他的动作都和浏览信息的动作差不多。我们来看这样的类,很容易看出它的一些缺点来:第一、它把权限计算和动作执行都放在一个类里,两者的功能相互混在一起,容易造成思路的混乱,而且修改维护和测试都不好;一句话来说, 它不满足单一职责原则 。第二是客户调用的时候 依赖具体的类,造成扩展和运行期内的调用的困难,不满足依赖颠倒原则 ( 依赖倒转原则(DIP)尽量依赖 抽象, 定义一个接口去实现它, 不要依赖 具体 )

    既然有这么多的问题,我们有必要对该类进行重新设计。其实大家早已想到,这个类应该使用代理模式。是啊,和我们买火车票的动作一样,动作类不能直接执行那个动作,而是要先检查权限,然后才能执行;先检查权限,后执行的那各类其实就是一个代理类,修改后的代码如下:

public interface Action
{
        public void doAction();
}

 

首先是设计一个接口,用来满足依赖颠倒原则。

Public class ViewAction implements Action
{
        public void doAction()
        {
               //做View的动作
               System.out.println(“You could view the information……”);
               ……
}
}

 

    这个类跟火车站一样,是动作的真实执行者。 <!---->
Public class ProxyViewAction implements Action
{
        private Action action = new ViewAction();
        public void doAction()
        {
               //调用权限类的方法取得用户权限
               if(Permission.getPermission(userId).equals(Constants.VIEW))
               {
                      action.doAction();
}
}
}
  这是代理类,很容易理解。在我们的ProxyViewAction类中,除了做了客户真正想要做的动作:doAction()以外,还进行了 额外 的动作检查用户的权限。而作核心动作doAction()是在一个 干干净净 的类:ViewAction中进行,这个类只做 核心动作 ,对其他的不关心,满足了单一职责原则。      客户端通过调用代理类来执行动作 ,而代理类一是将权限判断和动作的执行分离开来,满足了单一职责原则;二是实现了 一个接口,从而满足了依赖颠倒原则。比第一个思路好了很多。     代理又被称为 委派 ,说的是代理类 并不真正的执行那个核心动作 ,而是委派给另外一个类去执行,如ProxyView类中,ProxyView类并没有真正执行doAction()方法,而是交给ViewAction类去执行。     我们再来看代理类ProxyViewAction,可以看到它不仅依赖于接口Action,而且依赖于具体的实现ViewAction。这样对我们的系统扩展很不利,比如我们有Add动作、Delete动作、Modify动作等等,我们需要对每一个动作都写一个代理类,而这些代理类都做同样的事情,先进行权限判断,然后再委派。所以我们需要对这些代理再进行一次抽象, 让它只依赖接口Action,而不依赖于具体的实现

    要实现这样的想法,我们需要将代理类中的具体实现提走,让代理的使用者在运行期提供具体的实现类,即所谓的 依赖注入 ,如下:

Public class ProxyAction implements Action
{
        private Action action;
        public ProxyAction(Action action)
        {
               this.action = action;
}
        public void doAction()
        {
               //调用权限类的方法取得用户权限
               if(Permission.getPermission(userId).equals(action.getClass().getName()))
               {
                      action.doAction();
}
}
}

 

这样,我们就将所有实现了Action接口的实现使用一个代理类来代理它们。除了ViewAction类能用,以后扩展的AddAction、       ModifyAction、DeleteAction类等等,都可以使用一个代理类:ProxyAction。     而我们的客户端类似如下: Action action = ProxyAction(new ViewAction); Action.doAction();     通过对代理类的 依赖注入 ,我们使得代理类初步有了一定扩展性。但是我们还要看到,这个代理类依赖于某一个确定的接口。这仍然不能满足我们的实际要求,如我们的系统的权限控制一般是整个系统级的,这样系统级的权限控制,我们很难在整个系统里抽象出一个统一的接口,可能会有多个接口,按照上面的代理模式,我们需要对每一个接口写一个代理类,同样,这些类的功能都是一样的。这显然不是一个好地解决办法。     基于上面的原因,我们需要解决一个系统在没有统一的接口的情况下,对一些零散的对象的某一些动作使用代理模式的问题。JAVA API为我们引入了动态代理或动态委派的技术。     动态代理的核心是 InvocationHandler接口 ,要使用动态代理就必须实现该接口。这个接口的委派任务是在invoke(Object proxy, Method m, Object[] args)方法里面实现的: //invoke是调用的意思 //在调用核心功能之前作一些动作 …… //调用核心功能 m.invoke(obj, args); //在调用核心功能以后做一些动作 ……     我们可以看到动态代理其实用的是 反射机制 来调用核心功能的:m.invoke(obj, args);正是这种反射机制的使用使得我们调用核心功能更加灵活,而 不用依赖于某一个具体的接口,而是依赖于Object对象

    下面我们来具体看看动态代理或动态委派如何使用:

public class ProxyAction implements InvocationHandler {
private Object action;
//构造方法
public ProxyAction(Object action)
{
       this.action = action;
}
public static Object getInstance(Object action)
{
        return Proxy.newProxyInstance(action.getClass().getClassLoader(),
action.getClass().getInterfaces(),new ProxyAction(action));
}
 
public Object invoke(Object proxy, Method m, Object[] args)
               throws Throwable {
        
        Object result;
 
       try {
                      //在委派之前作动作,如权限判断等
           System.out.println("before method " + m.getName());
                      //进行委派
           result = m.invoke(action, args);
 
       } catch (InvocationTargetException e) {
 
           throw e.getTargetException();
 
       } catch (Exception e) {
 
           throw new RuntimeException("unexpected invocation exception: "
 
                  + e.getMessage());
 
       } finally {
                      //在委派之后做动作
           System.out.println("after method " + m.getName());
 
       }
 
       return result;
 
 
}
 
}

 

这个代理类,首先是实现了InvocationHandler接口;然后在getInstance()方法里得到了代理类的实例;在invoke()方法里实现代理功能,也很简单。     下面我们来看客户端: Action action = (Action)ProxyAction.getInstance(new ViewAction()); Action.doAction();     我们可以看到代理类对接口的依赖也转移到了客户端上,这样,代理类不依赖于某个接口。对于同样的代理类ProxyAction,我们也可以有如下的客户端调用: Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl()); Engine.execute();     只要engineImpl类实现了Engine接口,就可以像上面那样使用。

    现在我们可以看到,动态代理的确是拥有相当的灵活性。但我们同时也看到了,这个代理类写起来比较麻烦,而且也差不多每次都写这样千篇一律的东西,只有委派前的动作和委派后的动作在不同的代理里有着不同,其他的东西都需要照写。如果这样的代理类写多了,也会有一些冗余代理。需要我们进一步优化,这里我们使用模板方法模式来对这个代理类进行优化,如下:

<!---->
public abstract class BaseProxy implements InvocationHandler {
private Object obj;
protected BaseProxy(Object obj)
{
       this.obj = obj;
}
public static Object getInstance(Object obj,InvocationHandler instance)
{
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),instance);
}
 
public Object invoke(Object proxy, Method m, Object[] args)
               throws Throwable {
        // TODO Auto-generated method stub
        Object result;
 
       try {
 
           System.out.println("before method " + m.getName());
           this.doBegin();
 
           result = m.invoke(obj, args);
 
       } catch (InvocationTargetException e) {
 
           throw e.getTargetException();
 
       } catch (Exception e) {
 
           throw new RuntimeException("unexpected invocation exception: "
 
                  + e.getMessage());
 
       } finally {
 
           System.out.println("after method " + m.getName());
           this.doAfter();
 
       }
 
       return result;
 
 
}
public abstract void doBegin();
public abstract void doAfter();
 
}
 

这样,代理的实现类只需要关注实现委派前的动作和委派后的动作就行,如下

public class ProxyImpl extends BaseProxy {
protected ProxyImpl(Object o)
{
       super(o);
}
public static Object getInstance(Object foo)
{
        return getInstance(foo,new ProxyImpl(foo));
}
 
//委派前的动作
public void doBegin() {
        // TODO Auto-generated method stub
       System.out.println("begin doing....haha");
 
}
 
//委派后的动作
public void doAfter() {
        // TODO Auto-generated method stub
       System.out.println("after doing.....yeah");
 
}
 
}

 

从上面的代码,我们可以看出代理实现类的确是简单多了,只关注了委派前和委派后的动作,这是我们作为一个代理真正需要关心的。     至此,代理模式和动态代理已经告一段落。我们将动态代理引申一点说开去,来作为这篇文章的蛇足。     这个话题就是面向方面的编程,或者说AOP。我们看上面的ProxyImpl类,它的两个方法doBegin()和doAfter(),这是做核心动作之前和之后的两个截取段。正是这两个截取段,却是我们AOP的基础。在OOP里,doBegin(),核心动作,doAfter()这三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如doBegin()可能做的是权限,在所有的类里它都做权限;而在每个类里核心动作却各不相同;doAfter()可能做的是日志,在所有的类里它都做日志。正是因为在所有的类里,doBegin()或doAfter()都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析、设计和编码,这就是我们的AOP的思想。     这样说来,我们的动态代理就能作为实现AOP的基础了。好了,就说这么多,关于AOP技术,我们可以去关注关于这方面的知识。 <!----><!----><!----><!----><!----><!---->
43
11
分享到:
评论
28 楼 szhnet 2008-04-25  
楼主讲的很好呀
27 楼 senbao18 2008-04-24  
不好意思,这个文章当时发上来的时候一直是没有确定从哪里找到的,多亏了 fredzhang 提醒,才找到原文地址,现向大家说明一下,该文章转载,如有冒犯之处,还请见谅!http: //blog.csdn.net/hivon/archive/2006/01/11/576691.aspx
26 楼 gxiaochuan 2008-04-23  
beautiful
25 楼 fredzhang 2008-04-23  
这不是转载 blog.csdn.net/hivon 软件的信雅达 的文章吗?
24 楼 jonescheng 2008-04-17  
写的简洁深动,,,非常精彩,谢谢了
23 楼 lxdhdgss 2008-04-06  
22 楼 heroczx 2008-04-04  
转贴哦~~
21 楼 hgz123 2008-04-04  
不错啊`~怎么没写了呢`~
20 楼 jzhaoke 2008-04-03  
ProxyImpl
19 楼 qxwtt 2008-04-03  
很好 很生动
18 楼 qxwtt 2008-04-03  
bljl\sfdsfdsfsdfsdfsdfdsfsdfds
17 楼 playfish 2008-04-03  
发现楼主很强大。。博客点击数为1100,这篇文章点击率1099。。
16 楼 playfish 2008-04-03  
嗯,很生动,楼主辛苦了!
15 楼 星光闪烁 2008-04-02  
呵呵   看这样的文章得先好好看看java设计模式啊
14 楼 senbao18 2008-04-01  
InvocationHandler本身就是一个接口,在接口里只定义了一个invoke的方法就是invoke(Object proxy, Method method, Object[] args)
13 楼 xpf7622 2008-04-01  
文档好象没这么说,public class ProxyAction implements InvocationHandler {。。。}总觉得invoke方法的 Object Proxy参数没有用上,是不是代码设计的不合理。
12 楼 xpf7622 2008-04-01  
写的不错,但正如:bluemeteor 所说,
invoke(Object proxy, Method m, Object[] args)

如果有默认的构造函数的话,第一个参数应该是没用的。例子中好象也没有用到第一个参数。回去查看一下JDK API。
11 楼 marscom 2008-04-01  
先顶再看
10 楼 jmd 2008-03-31  
看不懂 
9 楼 shz2008bj 2008-03-31  
不错。。。支持。。。。。

相关推荐

    设计模式:可复用面向对象软件的基础--详细书签版

     当然还有一些提示和鼓励:第一次阅读此书时你可能不会完全理解它,但不必着急,我们在起初编写这本书时也没有完全理解它们!请记住,这不是一本读完一遍就可以束之高阁的书。我们希望你在软件设计过程中反复参阅...

    设计模式可复用面向对象软件的基础.zip

    第1章 引言 1 1.1 什么是设计模式 2 1.2 Smalltalk MVC中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 ...

    EduSoho网校系统 V8.2.25

    新增:ES 分销:支持一级代理用户拉新功能 新增:搜索功能支持班级搜索 优化:系统后台账务设置 修复:已知问题 8.2.21 (2018-3-15) 修复:少数情况下课程有效期天数修改失效的问题 修复:使用第三方平台注册...

    适配器模式和代理模式的区别

    代理模式 组成: 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。 真实角色:实现抽象...

    Blogs:您好,这是panyz的博客:grinning_face::upside-down_face:

    Hello,这里是panyz的博客:grinning_face::upside-down_face:《Android群英传》第5章 Android Scroll分析《Android源码设计模式解析与实战》第1章 面向对象的六大原则《Android源码设计模式解析与实战》第2章 单例...

    设计模式--可复用面向对象软件的基础

    第一章 引言 1.1 什么是设计模式 1.2 Smalltalk MVC中的设计模式 1.3 描述设计模式 1.4 设计模式的编目 1.5 组织编目 1.6 设计模式怎样解决设计问题 1.7 怎样选择设计模式 1.8 怎样使用设计模式 第二章 实例研究:...

    ActionScript 3.0设计模式扫描版_方红琴译

    第1章 面向对象程序设计、设计模式和ActionScript 3.0 成功之后的喜悦 OOP基础 抽象 封装 继承 多态性 使用设计模式进行软件开发的原则 使用接口而不是具体的实现 养成使用组合的习惯 计划项目时要同时...

    C#面向对象设计模式纵横谈\13 结构型模式Proxy代理模式.zip

    在这里与各位分享本人从网络上下载的C#面向对象设计模式纵横谈系列视频,共有25节,除了第一节需要各位贡献一点资源分以作为对本人上传资源的回馈,后面的其他资源均不需要资源分。敬请期待。 这是第13节:结构型...

    Mastering-Python-Design-Patterns-Second-Edition:Packt出版的Mastering-Python-Design-Patterns-Second-Edition

    精通Python设计模式,第二版... 本书涵盖以下激动人心的功能: 探索工厂方法和抽象工厂以创建对象使用原型模式克隆对象使用适配器模式使不兼容的接口兼容使用代理模式保护接口使用策略模式动态选择算法如果您觉得这本书

    Delphi模式编程第一分卷

    一共两个压缩分卷,这是第一个分卷 第一部分 模式编程原理 第1章 模式概述 1.1 模式的概念 1.1.1 什么是模式 1.1.2 模式可以做什么 1.2 模式与架构 1.2.1 什么是架构 1.2.2 架构和模式的关系 1.3 从面向...

    Python_Master-the-Art-of-Design-Patterns:Packt编写的Python Master of Design Patterns的代码库

    使用结构设计模式,找出对象和类如何交互以构建更大的应用程序使用Python设计模式提高应用程序的生产力和代码库使用代理模式保护接口###其他信息模块1:第1章和第2章没有代码文件模块2:第11章中没有代码文件###...

    C#设计模式 C# Design Patterns:A Tutorial

    第一部分 C#面向对象程序设计 第1章 设计模式概述 第2章 C#语言的语法 第3章 用C#编写Windows程序 第4章 在C#中使用类和对象 第5章 继承 · · · · · · (更多) 第一部分 C#面向对象程序设计 第1章 设计模式概述...

    《设计模式可复用面向对象软件的基础》

    目录-------------------------------------------序言前言读者指南第1章 引言 11.1 什么是设计模式 21.2 Smalltalk MVC中的设计模式 31.3 描述设计模式 41.4 设计模式的编目 51.5 组织编目 71.6 设计模式怎样解决...

    Delphi模式编程第二分卷

    第一部分 模式编程原理 第1章 模式概述 1.1 模式的概念 1.1.1 什么是模式 1.1.2 模式可以做什么 1.2 模式与架构 1.2.1 什么是架构 1.2.2 架构和模式的关系 1.3 从面向对象编程到模式编程 1.3.1 ...

    设计模式:可复用面向对象软件的基础

    第1章 引言 1 1.1 什么是设计模式 2 1.2 Smalltalk MVC中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 ...

    kotlin-video-tutorial:Kotlin视频教程

    Kotlin基础视频教程(第一版) 从5月份开始制作这个视频,历时两个多月,终于在网易云课堂与大家见面了... 视频特色 在前期课程中,使用普通编辑器进行编码,治疗你的IDE依赖症 所有课程均使用live coding的方式授课...

    何红辉关爱民-Android源码设计模式解析与实战(带书签第2版).pdf

    优化代码的第一步、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则、单例模式、Builder模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、...

    设计模式课程每章ppt(共27章).zip

    ├── 01_第1章_统一建模语言基础知识.ppt ├── 第10章_适配器模式.ppt ├── 第11章_桥接模式.ppt ├── 第12章_组合模式.ppt ├── 第13章_装饰模式.ppt ├── 第14章_外观模式.ppt ├── 第15章_享元模式...

Global site tag (gtag.js) - Google Analytics