Design Pattern: Proxy 模式(二)

來源:互聯網
上載者:User

http://www.35java.com/zhibo/forum.php?mod=viewthread&tid=278&extra=page%3D1

 

延續 Proxy模式(一) 的議題,來看看實現代理的兩種方式:Static Proxy與Dynamic Proxy。嚴格來說這是屬於模式的實現方式,不過藉由執行個體可以更瞭解Proxy模式的應用。

先來看個例子,這個例子是記錄(log)動作,程式中很常需要為某些動作或事件作下記錄,以便在事後檢視或是作為除錯時的資訊,一個最簡單的例子如下:

  • 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在執行hello()方法時,您希望能記錄該方法已經執行及結束,最簡單的作法就是如上在執行的前後加上記錄動作,然而Logger介入了HelloSpeaker中,記錄這個動作並不屬於HelloSpeaker,這使得HelloSpeaker增加了非業務上需要的邏輯在當中。

想想如果程式中這種記錄的動作到處都有需求,上面這種寫法勢必造成必須複製記錄動作的程式碼,使得維護記錄動作的困難度加大。如果不只有記錄動作,有一些非物件本身職責的相關動作也混入了物件之中(例如許可權檢查、交易管理等等),會使得物件的負擔更形加重,甚至混淆了物件的職責,物件本身的職責所佔的程式碼,或許遠小於這些與物件職責不相關動作的程式碼。

怎麼辦,用下面的方法或許好一些,先定義一個介面,然後實作該介面:

  • 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);
    }
}

接下來實作一個代理物件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....");
    }
}

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

代理物件HelloProxy將代理真正的HelloSpeaker來執行hello(),並在其前後加上記錄的動作,這使得 HelloSpeaker在撰寫時不必介入記錄動作,HelloSpeaker可以專心於它的職責。

這是Static Proxy的基本範例,然而如您所看到的,代理物件的一個介面只服務於一種類型的物件,而且如果要代理的方法很多,勢必要為每個方法進行代理, Static Proxy在程式規模稍大時就必定無法勝任。

Java在JDK 1.3之後加入協助開發Dynamic Proxy功能的類別,我們不必為特定物件與方法撰寫特定的代理,使用DynamicProxy,可以使得一個handler服務於各個物件,首先,一個handler必須實現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()方法會傳入被代理物件的方法名稱與執行參數實際上要執行的方法交由method.invoke(),並在其前後加上記錄動作,method.invoke()傳回的物件是實際方法執行過後的回傳結果。

Dynamic Proxy必須宣告介面,實作該介面,例如:

  • 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產生一個代理物件,我們可以使用下面的方法來執行程式:
LogHandler logHandler  = new LogHandler();
IHello helloProxy = (IHello) logHandler.bind(
                                  new HelloSpeaker());
helloProxy.hello("Justin");

LogHandler不在服務於特定物件與介面,而HelloSpeaker也不用插入任何有關於記錄的動作,它不用意識到記錄動作的存在。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.