Calvin's Tech Space

          成于堅(jiān)忍,毀于浮躁

             :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
           

          代理模式、動(dòng)態(tài)代理和面向方面

              代理的意思很好理解,它借了我日常所用的代理的意思:就是本來(lái)自己自去做的某件事,由于某種原因不能直接做,而只能請(qǐng)人代替你做,個(gè)被你請(qǐng)來(lái)做事的人就是代理。比如過(guò)節(jié)要回家,由于你要上班,沒(méi)時(shí)間買(mǎi)票,就得票務(wù)中介代你購(gòu)買(mǎi)就是一種代理模式。個(gè)情景可以形象的描述如下:

          class:火車(chē)

          {

                  賣(mài)票:

                 {……}

          }

              車(chē)站是賣(mài)票的地方,我設(shè)只能在火車(chē)買(mǎi)到票。賣(mài)票的動(dòng)實(shí)質(zhì)是火車(chē)類(lèi)完成的。

          Class:票務(wù)中介

          {

                  賣(mài)票:

                  {

                         收中介費(fèi)

                        車(chē).賣(mài)票;

          }

          }

              客找票務(wù)中介買(mǎi)票的時(shí)候,調(diào)用票務(wù)中介.賣(mài)票。票務(wù)中介其實(shí)做了兩件事,一是去火車(chē)買(mǎi)票,二是不能白幫你賣(mài)票,肯定要收中介費(fèi)。而你得到的好是不用直接去火車(chē)買(mǎi)票,節(jié)省了買(mǎi)票的時(shí)間用來(lái)上班。

              以上我們簡(jiǎn)單了代理模式的情景和什么要使用代理模式,下面我以一個(gè)例子來(lái)具體分析一下JAVA中的代理模式。

              設(shè)有一個(gè)信息管理系統(tǒng),用些用瀏覽信息的權(quán)限,有些用瀏覽、添加和修改信息的權(quán)限,有些用有除了上述的權(quán)限,除信息的權(quán)限,那么我最容易想到的做法如下:

          public class ViewAction

          {

                  //userId計(jì)權(quán)

                  ……

                  String permission = ……;

                 if(permission.equals(Constants.VIEW))

                  {

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

                         ……

          }

          }

              其他的動(dòng)作都和瀏覽信息的動(dòng)作差不多。我來(lái)看這樣類(lèi),很容易看出它的一些缺點(diǎn)來(lái):第一、它把權(quán)計(jì)算和動(dòng)執(zhí)行都放在一個(gè)類(lèi)里,兩者的功能相互混在一起,容易造成思路的混亂,而且修改維護(hù)測(cè)試都不好;一句來(lái)說(shuō),它不滿職責(zé)。第二是客戶調(diào)用的時(shí)候依具體的類(lèi),造成擴(kuò)展和運(yùn)行期內(nèi)的調(diào)用的困,不滿足依賴顛倒原

              既然有么多的問(wèn)題,我有必要對(duì)該類(lèi)進(jìn)行重新設(shè)計(jì)。其實(shí)大家早已想到,個(gè)類(lèi)應(yīng)該使用代理模式。是啊,和我們買(mǎi)車(chē)票的動(dòng)作一動(dòng)類(lèi)不能直接執(zhí)行那個(gè)動(dòng)作,而是要先檢查權(quán)限,然后才能執(zhí)行;先檢查權(quán)限,后執(zhí)行的那各類(lèi)實(shí)就是一個(gè)代理類(lèi),修改后的代如下:

          public interface Action

          {

                  public void doAction();

          }

             首先是設(shè)計(jì)一個(gè)接口,用來(lái)滿足依賴顛倒原

          Public class ViewAction implements Action

          {

                  public void doAction()

                  {

                         //View動(dòng)

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

                         ……

          }

          }

              個(gè)類(lèi)跟火車(chē)站一,是動(dòng)作的真實(shí)執(zhí)行者。

          Public class ProxyViewAction implements Action

          {

                  private Action action = new ViewAction();

                  public void doAction()

                  {

                         //調(diào)權(quán)類(lèi)的方法取得用戶權(quán)

                         if(Permission.getPermission(userId).equals(Constants.VIEW))

                         {

                                action.doAction();

          }

          }

          }

              是代理類(lèi),很容易理解。在我ProxyViewAction類(lèi)中,除了做了客真正想要做的動(dòng)作:doAction()以外,還進(jìn)行了外的動(dòng)檢查權(quán)限。而作核心動(dòng)doAction()是在一個(gè)干干凈凈類(lèi)ViewAction進(jìn)行,個(gè)類(lèi)只做核心動(dòng)作,對(duì)其他的不關(guān)心,滿足了職責(zé)

              端通過(guò)調(diào)用代理類(lèi)來(lái)執(zhí)動(dòng)作,而代理類(lèi)一是將權(quán)限判斷和動(dòng)作的執(zhí)行分離開(kāi)來(lái),滿足了職責(zé);二是實(shí)現(xiàn)了一個(gè)接口,從而滿足了依賴顛倒原。比第一個(gè)思路好了很多。

              代理又被稱委派,說(shuō)的是代理類(lèi)并不真正的執(zhí)行那個(gè)核心動(dòng)作,而是委派另外一個(gè)類(lèi)執(zhí)行,如ProxyView類(lèi)中,ProxyView類(lèi)并沒(méi)有真正執(zhí)doAction()方法,而是交ViewAction類(lèi)執(zhí)行。

              再來(lái)看代理類(lèi)ProxyViewAction,可以看到它不于接口Action,而且依于具體的實(shí)現(xiàn)ViewAction這樣對(duì)的系統(tǒng)擴(kuò)展很不利,比如我Add動(dòng)作、Delete動(dòng)作、Modify動(dòng)作等等,我需要對(duì)每一個(gè)動(dòng)作都寫(xiě)一個(gè)代理類(lèi),而些代理類(lèi)都做同的事情,先進(jìn)權(quán)限判斷,然后再委派。所以我需要對(duì)這些代理再進(jìn)行一次抽象,它只依接口Action,而不依于具體的實(shí)現(xiàn)

              實(shí)現(xiàn)這樣的想法,我需要將代理類(lèi)中的具體實(shí)現(xiàn)提走,代理的使用者在運(yùn)行期提供具體的實(shí)現(xiàn)類(lèi),即所的依注入,如下:

          Public class ProxyAction implements Action

          {

                  private Action action;

                  public ProxyAction(Action action)

                  {

                         this.action = action;

          }

                  public void doAction()

                  {

                         //調(diào)權(quán)類(lèi)的方法取得用戶權(quán)

                         if(Permission.getPermission(userId).equals(action.getClass().getName()))

                         {

                                action.doAction();

          }

          }

          }

              這樣,我就將所有實(shí)現(xiàn)Action接口的實(shí)現(xiàn)使用一個(gè)代理類(lèi)來(lái)代理它。除了ViewAction類(lèi)能用,以后擴(kuò)展的AddAction       ModifyActionDeleteAction類(lèi)等等,都可以使用一個(gè)代理類(lèi)ProxyAction

              而我的客類(lèi)似如下:

          Action action = ProxyAction(new ViewAction);

          Action.doAction();

              過(guò)對(duì)代理類(lèi)的依注入,我使得代理類(lèi)初步有了一定擴(kuò)展性。但是我們還要看到,個(gè)代理類(lèi)于某一個(gè)確定的接口。仍然不能滿足我實(shí)際要求,如我的系統(tǒng)權(quán)限控制一般是整個(gè)系統(tǒng)級(jí)的,這樣統(tǒng)級(jí)權(quán)限控制,我在整個(gè)系統(tǒng)里抽象出一個(gè)統(tǒng)一的接口,可能會(huì)有多個(gè)接口,按照上面的代理模式,我需要對(duì)每一個(gè)接口寫(xiě)一個(gè)代理類(lèi),同類(lèi)的功能都是一的。這顯然不是一個(gè)好地解決法。

              基于上面的原因,我需要解決一個(gè)系統(tǒng)在沒(méi)有統(tǒng)一的接口的情況下,對(duì)一些零散的對(duì)象的某一些動(dòng)作使用代理模式的問(wèn)題JAVA API引入了動(dòng)態(tài)代理或動(dòng)態(tài)委派的技術(shù)

              動(dòng)態(tài)代理的核心是InvocationHandler接口,要使用動(dòng)態(tài)代理就必須實(shí)現(xiàn)該接口。個(gè)接口的委派任務(wù)是在invoke(Object proxy, Method m, Object[] args)方法里面實(shí)現(xiàn)的:

          //調(diào)用核心功能之前作一些動(dòng)

          ……

          //調(diào)用核心功能

          m.invoke(obj, args);

          //調(diào)用核心功能以后做一些動(dòng)

          ……

              可以看到動(dòng)態(tài)代理其實(shí)用的是反射機(jī)制來(lái)調(diào)用核心功能的:m.invoke(obj, args);正是種反射機(jī)制的使用使得我們調(diào)用核心功能更加靈活,而不用依于某一個(gè)具體的接口,而是依Object對(duì)象。

              下面我來(lái)具體看看動(dòng)態(tài)代理或動(dòng)態(tài)委派如何使用:

          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 {

                                //在委派之前作動(dòng)作,如權(quán)限判斷等

                     System.out.println("before method " + m.getName());

                                //進(jìn)行委派

                     result = m.invoke(action, args);

                 } catch (InvocationTargetException e) {

                     throw e.getTargetException();

                 } catch (Exception e) {

                     throw new RuntimeException("unexpected invocation exception: "

                            + e.getMessage());

                 } finally {

                                //在委派之后做動(dòng)

                     System.out.println("after method " + m.getName());

                 }

                 return result;

           

          }

          }

              個(gè)代理類(lèi)首先是實(shí)現(xiàn)InvocationHandler接口;然后在getInstance()方法里得到了代理類(lèi)實(shí)例;在invoke()方法里實(shí)現(xiàn)代理功能,也很簡(jiǎn)單

              下面我來(lái)看客端:

          Action action = (Action)ProxyAction.getInstance(new ViewAction());

          Action.doAction();

              可以看到代理類(lèi)對(duì)接口的依轉(zhuǎn)移到了客端上,這樣,代理類(lèi)不依于某個(gè)接口。對(duì)于同的代理類(lèi)ProxyAction,我也可以有如下的客調(diào)用:

          Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl());

          Engine.execute();

              只要engineImpl類(lèi)實(shí)現(xiàn)Engine接口,就可以像上面那使用。

              現(xiàn)在我可以看到,動(dòng)態(tài)代理的確是有相當(dāng)?shù)撵`活性。但我時(shí)也看到了,個(gè)代理類(lèi)寫(xiě)起來(lái)比,而且也差不多每次都寫(xiě)這樣千篇一律的西,只有委派前的動(dòng)作和委派后的動(dòng)作在不同的代理里有著不同,其他的西都需要照寫(xiě)。如果這樣的代理類(lèi)寫(xiě)多了,也會(huì)有一些冗余代理。需要我們進(jìn)一步優(yōu)化,里我使用模板方法模式來(lái)對(duì)這個(gè)代理類(lèi)進(jìn)優(yōu)化,如下:

          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();

          }

              這樣,代理的實(shí)現(xiàn)類(lèi)只需要關(guān)注實(shí)現(xiàn)委派前的動(dòng)作和委派后的動(dòng)作就行,如下:

          public class ProxyImpl extends BaseProxy {

          protected ProxyImpl(Object o)

          {

                 super(o);

          }

          public static Object getInstance(Object foo)

          {

                  return getInstance(foo,new ProxyImpl(foo));

          }

          //委派前的動(dòng)

          public void doBegin() {

                  // TODO Auto-generated method stub

                 System.out.println("begin doing....haha");

          }

          //委派后的動(dòng)

          public void doAfter() {

                  // TODO Auto-generated method stub

                 System.out.println("after doing.....yeah");

          }

          }

              從上面的代,我可以看出代理實(shí)現(xiàn)類(lèi)的確是簡(jiǎn)單多了,只關(guān)注了委派前和委派后的動(dòng)作,是我一個(gè)代理真正需要關(guān)心的。

              至此,代理模式和動(dòng)態(tài)代理已經(jīng)告一段落。我動(dòng)態(tài)代理引申一點(diǎn)說(shuō)開(kāi)去,來(lái)作為這篇文章的蛇足。

              個(gè)話題就是面向方面的程,或者說(shuō)AOP。我看上面的ProxyImpl類(lèi),它的兩個(gè)方法doBegin()doAfter()是做核心動(dòng)作之前和之后的兩個(gè)截取段。正是兩個(gè)截取段,卻是我AOP的基礎(chǔ)。在OOP里,doBegin(),核心動(dòng)作,doAfter()三個(gè)動(dòng)作在多個(gè)類(lèi)里始在一起,但他所要完成的邏輯卻是不同的,如doBegin()可能做的是權(quán)限,在所有的類(lèi)里它都做權(quán)限;而在每個(gè)類(lèi)里核心動(dòng)作卻各不相同;doAfter()可能做的是日志,在所有的類(lèi)里它都做日志。正是因在所有的類(lèi)里,doBegin()doAfter()都做的是同邏輯,因此我需要將它提取出來(lái),獨(dú)分析、設(shè)計(jì)編碼就是我AOP的思想。

              這樣說(shuō)來(lái),我動(dòng)態(tài)代理就能作為實(shí)現(xiàn)AOP的基礎(chǔ)了。好了,就說(shuō)這么多,關(guān)于AOP術(shù),我可以去關(guān)注關(guān)于方面的知識(shí)

          posted on 2009-08-12 16:51 calvin 閱讀(237) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Design Patterns

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 邢台县| 峨眉山市| 扬州市| 扎兰屯市| 芮城县| 桂阳县| 正宁县| 新昌县| 淮南市| 民县| 石首市| 正镶白旗| 马关县| 贺兰县| 公安县| 平乐县| 齐河县| 集安市| 临城县| 伊春市| 盘山县| 葫芦岛市| 米易县| 密山市| 潼关县| 梨树县| 揭阳市| 陵川县| 茌平县| 秦安县| 扎兰屯市| 渝北区| 顺义区| 泽州县| 固阳县| 桐乡市| 牡丹江市| 长春市| 喀喇| 双江| 长寿区|