Calvin's Tech Space

成于坚忍,毁于浮躁

   :: 首页 :: 联系 :: 聚合  :: 管理
 

代理模式、动态代理和面向方面

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

class:火

{

        票:

       {……}

}

    站是票的地方,我只能在火到票。票的实质是火完成的。

Class:票中介

{

        票:

        {

               收中介

              .票;

}

}

    客找票中介票的候,用票中介.票。票中介其做了两件事,一是去火票,二是不能白帮你票,肯定要收中介。而你得到的好是不用直接去火票,省了票的时间用来上班。

    以上我们简单了代理模式的情景和什么要使用代理模式,下面我以一个例子来具体分析一下JAVA中的代理模式。

    有一个信息管理系,用些用浏览信息的限,有些用浏览、添加和修改信息的限,有些用有除了上述的限,除信息的限,那么我最容易想到的做法如下:

public class ViewAction

{

        //userId

        ……

        String permission = ……;

       if(permission.equals(Constants.VIEW))

        {

               System.out.println(“You could view the information……”);

               ……

}

}

    其他的作都和浏览信息的作差不多。我来看这样,很容易看出它的一些缺点来:第一、它把算和行都放在一个里,两者的功能相互混在一起,容易造成思路的混乱,而且修改维护测试都不好;一句,它不职责。第二是客户调用的候依具体的,造成展和运行期内的用的困,不足依赖颠倒原

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

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       ModifyActionDeleteAction等等,都可以使用一个代理ProxyAction

    而我的客似如下:

Action action = ProxyAction(new ViewAction);

Action.doAction();

    过对代理的依注入,我使得代理初步有了一定展性。但是我们还要看到,个代理于某一个确定的接口。仍然不能足我实际要求,如我的系限控制一般是整个系统级的,这样统级限控制,我在整个系里抽象出一个一的接口,可能会有多个接口,按照上面的代理模式,我需要每一个接口写一个代理,同的功能都是一的。这显然不是一个好地解决法。

    基于上面的原因,我需要解决一个系在没有一的接口的情况下,一些零散的象的某一些作使用代理模式的问题JAVA API引入了动态代理或动态委派的技

    动态代理的核心是InvocationHandler接口,要使用动态代理就必须实现该接口。个接口的委派任是在invoke(Object proxy, Method m, Object[] args)方法里面实现的:

//用核心功能之前作一些

……

//用核心功能

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,我可以去关注关于方面的知

posted on 2009-08-12 16:51 calvin 阅读(219) 评论(0)  编辑  收藏 所属分类: Design Patterns

只有注册用户登录后才能发表评论。


网站导航: