細(xì)心!用心!耐心!

          吾非文人,乃市井一俗人也,讀百卷書(shū),跨江河千里,故申城一游; 一兩滴辛酸,三四年學(xué)業(yè),五六點(diǎn)粗墨,七八筆買賣,九十道人情。

          BlogJava 聯(lián)系 聚合 管理
            1 Posts :: 196 Stories :: 10 Comments :: 0 Trackbacks
          延續(xù) Proxy模式(一) 的議題,來(lái)看看實(shí)現(xiàn)代理的兩種方式:Static Proxy與Dynamic Proxy。嚴(yán)格來(lái)說(shuō)這是屬於模式的實(shí)現(xiàn)方式,不過(guò)藉由實(shí)例可以更瞭解Proxy模式的應(yīng)用。

          先來(lái)看個(gè)例子,這個(gè)例子是記錄(log)動(dòng)作,程式中很常需要為某些動(dòng)作或事件作下記錄,以便在事後檢視或是作為除錯(cuò)時(shí)的資訊,一個(gè)最簡(jiǎn)單的例子如下:
          • HelloSpeaker.java
          import java.util.logging.*; 

          public class HelloSpeaker {
          private Logger logger =
          Logger.getLogger(this.getClass().getName());

          public void hello(String name) {
          logger.log(Level.INFO, "hello method starts....");

          System.out.println("Hello, " + name);

          logger.log(Level.INFO, "hello method ends....");
          }
          }

          HelloSpeaker在執(zhí)行hello()方法時(shí),您希望能記錄該方法已經(jīng)執(zhí)行及結(jié)束,最簡(jiǎn)單的作法就是如上在執(zhí)行的前後加上記錄動(dòng)作,然而 Logger介入了HelloSpeaker中,記錄這個(gè)動(dòng)作並不屬於HelloSpeaker,這使得HelloSpeaker增加了非業(yè)務(wù)上需要的邏輯在當(dāng)中。

          想想如果程式中這種記錄的動(dòng)作到處都有需求,上面這種寫(xiě)法勢(shì)必造成必須複製記錄動(dòng)作的程式碼,使得維護(hù)記錄動(dòng)作的困難度加大。如果不只有記錄動(dòng)作,有一些非物件本身職責(zé)的相關(guān)動(dòng)作也混入了物件之中(例如權(quán)限檢查、事務(wù)管理等等),會(huì)使得物件的負(fù)擔(dān)更形加重,甚至混淆了物件的職責(zé),物件本身的職責(zé)所佔(zhàn)的程式碼,或許遠(yuǎn)小於這些與物件職責(zé)不相關(guān)動(dòng)作的程式碼。

          怎麼辦,用下面的方法或許好一些,先定義一個(gè)介面,然後實(shí)作該介面:
          • IHello.java
          public interface IHello { 
          public void hello(String name);
          }

          • HelloSpeaker.java
          public class HelloSpeaker implements IHello { 
          public void hello(String name) {
          System.out.println("Hello, " + name);
          }
          }

          接下來(lái)實(shí)作一個(gè)代理物件HelloProxy:
          • HelloProxy.java
          import java.util.logging.*; 

          public class HelloProxy implements IHello {
          private Logger logger =
          Logger.getLogger(this.getClass().getName());
          private IHello helloObject;

          public HelloProxy(IHello helloObject) {
          this.helloObject = helloObject;
          }

          public void hello(String name) {
          logger.log(Level.INFO, "hello method starts....");

          helloObject.hello(name);

          logger.log(Level.INFO, "hello method ends....");
          }
          }

          執(zhí)行時(shí)可以如此:
          IHello helloProxy = new HelloProxy(new HelloSpeaker());
          helloProxy.hello("Justin");

          代理物件HelloProxy將代理真正的HelloSpeaker來(lái)執(zhí)行hello(),並在其前後加上記錄的動(dòng)作,這使得 HelloSpeaker在撰寫(xiě)時(shí)不必介入記錄動(dòng)作,HelloSpeaker可以專心於它的職責(zé)。

          這是Static Proxy的基本範(fàn)例,然而如您所看到的,代理物件的一個(gè)介面只服務(wù)於一種類型的物件,而且如果要代理的方法很多,勢(shì)必要為每個(gè)方法進(jìn)行代理, Static Proxy在程式規(guī)模稍大時(shí)就必定無(wú)法勝任。

          Java在JDK 1.3之後加入?yún)f(xié)助開(kāi)發(fā)Dynamic Proxy功能的類別,我們不必為特定物件與方法撰寫(xiě)特定的代理,使用Dynamic Proxy,可以使得一個(gè)handler服務(wù)於各個(gè)物件,首先,一個(gè)handler必須實(shí)現(xiàn) java.lang.reflect.InvocationHandler:
          • LogHandler.java
          import java.util.logging.*; 
          import java.lang.reflect.*;

          public class LogHandler implements InvocationHandler {
          private Logger logger =
          Logger.getLogger(this.getClass().getName());
          private Object delegate;

          public Object bind(Object delegate) {
          this.delegate = delegate;
          return Proxy.newProxyInstance(
          delegate.getClass().getClassLoader(),
          delegate.getClass().getInterfaces(),
          this);
          }

          public Object invoke(Object proxy,
          Method method,
          Object[] args) throws Throwable {
          Object result = null;
          try {
          logger.log(Level.INFO,
          "method starts..." + method);
          result = method.invoke(delegate, args);
          logger.log(Level.INFO,
          "method ends..." + method);
          } catch (Exception e){
          logger.log(Level.INFO, e.toString());
          }
          return result;
          }
          }

          InvocationHandler的invoke()方法會(huì)傳入被代理物件的方法名稱與執(zhí)行參數(shù)實(shí)際上要執(zhí)行的方法交由method.invoke (),並在其前後加上記錄動(dòng)作,method.invoke()傳回的物件是實(shí)際方法執(zhí)行過(guò)後的回傳結(jié)果。

          Dynamic Proxy必須宣告介面,實(shí)作該介面,例如:
          • IHello.java
          public interface IHello { 
          public void hello(String name);
          }

          • HelloSpeaker.java
          public class HelloSpeaker implements IHello { 
          public void hello(String name) {
          System.out.println("Hello, " + name);
          }
          }

          java.lang.reflect.Proxy的newProxyInstance()依要代理的物件、介面與handler產(chǎn)生一個(gè)代理物件,我們可以使用下面的方法來(lái)執(zhí)行程式:
          LogHandler logHandler = new LogHandler();
          IHello helloProxy = (IHello) logHandler.bind(
          new HelloSpeaker());
          helloProxy.hello("Justin");

          LogHandler不在服務(wù)於特定物件與介面,而HelloSpeaker也不用插入任何有關(guān)於記錄的動(dòng)作,它不用意識(shí)到記錄動(dòng)作的存在。
          posted on 2007-04-17 10:38 張金鵬 閱讀(495) 評(píng)論(0)  編輯  收藏 所屬分類: Structural 模式
          主站蜘蛛池模板: 边坝县| 松江区| 成都市| 隆子县| 招远市| 屯留县| 武平县| 曲靖市| 石河子市| 肥西县| 泉州市| 丰城市| 临海市| 新蔡县| 惠安县| 永安市| 乌兰察布市| 莱阳市| 建平县| 广河县| 邓州市| 浦城县| 龙井市| 横山县| 夏邑县| 会宁县| 天全县| 新竹市| 方正县| 河池市| 西贡区| 讷河市| 罗田县| 大渡口区| 元江| 泊头市| 沿河| 松桃| 乌恰县| 武安市| 夏河县|