設計模式_JAVA動態代理設計模式

Mindy_Lou發表於2017-01-02

 一、  什麼是代理設計模式

為其他物件提供一種代理以控制對這個物件的訪問。

二、  靜態代理設計模式

在以後的日子裡,我會用學習剩下的時間和大家一起學習Java設計模式,書寫有誤之處,還望指正!謝謝!

我們的模式之旅,從這裡開始

(注意:模式講解過程會涉及到spring,struts,hibernate,jsf的一些東西,因為我相信這樣做是有益的呵呵)

程式碼一

日誌實現的一般方法:


import java.util.logging.*;

public class HelpSpeaker {

    public static void main(String[] name) {
        
        HelpSpeaker help=new HelpSpeaker();
        
        Logger logger=
            Logger.getLogger(help.getClass().getName());
        
        //方法執行開始時留下記錄
        logger.log(Level.INFO,"hello method starts  ");
        
        //程式的主要功能
        System.out.println("Hello");
        
        
        //方法執行完畢前留下記錄
        logger.log(Level.INFO,"hello method ends  ");

    }
}


這樣寫的好處是簡單,當我們的程式碼量不多的時候,呵呵這樣寫無疑是首選。

     
這樣寫,我們必須在每個程式碼裡都寫上這些內容,當我們的程式碼量多起來的時候,比如,100個程式碼裡面需要記錄日誌,想想需要多大的工作量,再想想,但我們在想在日誌裡新增一些內容的時候,或者需要去掉或分類管理的話,那有多亂,這樣重複而無聊的工作,是多麼另人望而生畏!!!



程式碼二

由於這種做法的侷限性不合理性於是出現了代理;下面我介紹下我程式碼中的角色和物件:

/*
*
電腦批發商
**/

public interface Computer {
    public void buy(String name);
}

 

/*
*
聯想電腦公司
**/

public class Lianxiang implements Computer
{

    public void buy(String name) 
    {
      System.out.println(name+"  聯想電腦公司產品!");
    }

}

 

 1/*
 2*三星電腦公司
 3**/
 4public class Sanxing implements Computer {
 5
 6    public void buy(String name) {
 7       
 8       System.out.println(name+"  三星電腦公司產品!");
 9    }
10
11}

 

/*
*
電腦銷售代理商
**/

import java.util.logging.Level;
import java.util.logging.Logger;

public class ComputerProxy implements Computer {

    private Logger logger=
            Logger.getLogger(this.getClass().getName());
    
    private Computer computer;
    
    public  ComputerProxy(Computer helpObject){
        this.computer=helpObject;
    }
    
    public void buy(String name) {
           
        //方法執行開始時留下記錄
        logger.log(Level.INFO,"hello method starts  ");
        
        //程式的主要功能
        computer.buy(name);
        
        
        //方法執行完畢前留下記錄
        logger.log(Level.INFO,"hello method ends  ");
    }
    
    private void log(String msg){
        logger.log(Level.INFO,msg);
    }

 

/*
*
買電腦的客戶有兩個
**/

public class BuyComputer {

    public static void main(String[] args) {

        ComputerProxy proxy = new ComputerProxy(new Lianxiang());

        proxy.buy("我想買一臺聯想電腦");
        
        ComputerProxy proxy1 = new ComputerProxy(new Sanxing());

        proxy1.buy("我想買一臺三星電腦");

    }

}


執行結果:

我想買一臺聯想電腦  聯想電腦公司產品!
我想買一臺三星電腦  三星電腦公司產品!
2007-8-8 21:01:27 com.lusm.spring.ComputerProxy buy
資訊: hello method starts  
2007-8-8 21:01:28 com.lusm.spring.ComputerProxy buy
資訊: hello method ends  
2007-8-8 21:01:28 com.lusm.spring.ComputerProxy buy
資訊: hello method starts  
2007-8-8 21:01:28 com.lusm.spring.ComputerProxy buy
資訊: hello method ends  


很明顯,我們在main裡的程式碼非常之少,而且可以通過(一個或者多個)批發商改變很多,代理內容,比如,批發商可以加一些附加的服務(送網線,送滑鼠....),程式碼之間通過介面減低了程式塊間的偶合性;下面讓我們分析一下,程式碼是怎麼工作的?



程式碼分析:

仔細看完程式碼我們知道客戶買電腦是怎麼一個過程:

    
使用者找到代理商buy一臺自己想要買的電腦,通過這兩個語句,代理商知道使用者想要買什麼牌子的電腦

//想買聯想電腦
ComputerProxy proxy = new ComputerProxy(new Lianxiang());

//想買三星電腦
ComputerProxy proxy1 = new ComputerProxy(new Sanxing());


   
然後代理商根據使用者的需要,找到電腦批發商Computer(注意:由於代理商和批發商之間並沒有繼承關係extends,只是充當一個批發代理的角色implements,提醒一句,在Java裡面,我們完全可以把介面看成角色,把類看成角色的表現實體--物件

private Computer computer;


問批發商說:裡面這裡有沒有兩種電腦,一種叫聯想的電腦,一種叫三星電腦

public  ComputerProxy(Computer helpObject){
        this.computer=helpObject;
    }


批發商看了看自己手上的貨單說:有啊!我找兩臺給你!

public void buy(String name) {
           
        //方法執行開始時留下記錄
        logger.log(Level.INFO,"hello method starts  ");
        
        //程式的主要功能
        computer.buy(name);
        
        
        //方法執行完畢前留下記錄
        logger.log(Level.INFO,"hello method ends  ");
    }
private void log(String msg){
        logger.log(Level.INFO,msg);
    }


我看到,代理商業是個在經營方面,非常用心的人,在工作的時候總是帶一個筆記

private Logger logger=Logger.getLogger(this.getClass().getName());


在買的時候就用log方法把整個買個過成記錄下來。
 


呵呵!大家,我的故事講完了!希望大家用心體會,有什麼問題請留言!謝謝您的支援!!!

 

三、  動態代理設計模式

前面一個文章裡的程式碼很簡單(只是讓大家瞭解什麼是代理),實現的是靜態代理,做為電腦代理商的ComputerProxy,在電腦行業為電腦生產商(三星,聯想)和客戶提供服務,提供各種方便。
       
鬱悶的是,如果我現在增加一個行業,比如下面要講到的Car汽車行業,那麼,我們只能增加一個代理了,也就是說我們要再寫一個CarProxy程式碼,我們現在假設我們有很多個行業,那麼,無疑我們的工作量開始大了,有沒有什麼辦法讓我們的代理商實現跨行業代理呢?
       
答案是:可以。這就是我們這裡講的動態代理產生存在的意義了。

請看程式碼

在原有程式碼的基礎上我們做了這些寬展:

/*
*
汽車批發商
*
這樣我們的程式碼中就有了電腦和汽車這兩個批發商
*/

public interface Car {
    public void buyCar(String name);
}

 

/*
*
勞斯萊斯汽車公司
*/

public class RollsRoyce implements Car {

    public void buyCar(String name) {
        
        System.out.println(name+"  勞斯萊斯公司產品!");
    }

}

 

/*
*
所有行業代理商
*
有了它我們的客戶可以通過他買個各種產品
*/

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AllthingsProxy implements InvocationHandler {

    private Logger logger=
        Logger.getLogger(this.getClass().getName());
    
    private Object allthings;
    
    //實現物件繫結
    public Object bind(Object allthings){
        
        this.allthings = allthings;
        
        //這裡傳入newProxyInstance的引數分別是 目標object
        //
Lianxiang,Sanxing,interface(Computer),AllthingsProxy
        return Proxy.newProxyInstance(allthings.getClass().getClassLoader(),
                                    allthings.getClass().getInterfaces(), this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        
        Object result = null;
        
        try{
        log("method starts " + method);
        
        result=method.invoke(allthings, args);

        logger.log(Level.INFO , "method ends " + method);
        
        }catch(Exception e){
            log(e.toString());
        }
        
        return result;
    }
    
    private void log(String msg){
        logger.log(Level.INFO,msg);
    }

}



在測試類BuyAllThings中,我們通過bing方法繫結物件(所要買的東西),讓代理商瞭解到,客戶想買什麼?
(這裡重在瞭解模式,具體方法的實現如不瞭解請自行查詢API文件)


/*
*
三個客戶兩個買電腦一個買汽車
*
他們找到同個代理商
*/

public class BuyAllThing {

    public static void main(String[] args) {
        
        AllthingsProxy allthingsproxy = new AllthingsProxy();
        
        Computer SanxingProxy=(Computer)allthingsproxy.bind(new Sanxing());
        
        SanxingProxy.buyComputer("我想買一臺三星電腦");
        
        Computer lianxiangProxy=(Computer)allthingsproxy.bind(new Lianxiang());
        
        lianxiangProxy.buyComputer("我想買一臺聯想電腦");
        
        Car rRollsRoyceProxy=(Car)allthingsproxy.bind(new RollsRoyce());
        
        RollsRoyceProxy.buyCar("我想買一輛勞斯萊斯汽車");

    }

}


執行結果

我想買一臺三星電腦  三星電腦公司產品!
我想買一臺聯想電腦  聯想電腦公司產品!
我想買一輛勞斯萊斯汽車  勞斯萊斯公司產品!
2007-8-9 13:08:41 com.lusm.spring.AllthingsProxy log
資訊: method starts public abstract void com.lusm.spring.Computer.buyComputer(java.lang.String)
2007-8-9 13:08:42 com.lusm.spring.AllthingsProxy invoke
資訊: method ends public abstract void com.lusm.spring.Computer.buyComputer(java.lang.String)
2007-8-9 13:08:42 com.lusm.spring.AllthingsProxy log
資訊: method starts public abstract void com.lusm.spring.Computer.buyComputer(java.lang.String)
2007-8-9 13:08:42 com.lusm.spring.AllthingsProxy invoke
資訊: method ends public abstract void com.lusm.spring.Computer.buyComputer(java.lang.String)
2007-8-9 13:08:42 com.lusm.spring.AllthingsProxy log
資訊: method starts public abstract void com.lusm.spring.Car.buyCar(java.lang.String)
2007-8-9 13:08:42 com.lusm.spring.AllthingsProxy invoke
資訊: method ends public abstract void com.lusm.spring.Car.buyCar(java.lang.String)


我們可以任意的增加代理商的業務,比如,叫他代理電器,食物......,我們看到我們不需要更改原有的程式碼。這是動態代理帶來的好處!

那我們的AllthingsProxy是怎麼作到動態代理的呢?

AllthingsProxy
寬展了InvocationHandler並實現了裡面的代理方法,返回一個Object物件,

    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;


來實現對汽車,電腦這些批發商的動態代理(代理商同過它代理所有行業)。

AllthingsProxy
中的bind實現了客戶和代理商間的通訊(通過它代理商知道客戶想要買什麼)

這和我們 BuyAllThing測試類main

代理物件=(繫結物件)allthingsproxy.bind(繫結物件(客戶想買的東西))


想對應。

        
呵呵,講完了!也許有的朋友看不懂這裡在說什麼?不必著急,學習都需要過程,等你的學習到某個階段的時候,回頭想想,也許認識就會加深許多,本人覺得Java是比較高階的語言,自身的發展也只直遵循著軟體設計優化(程式碼重用)方向發展,重視設計思想,而不是去改變語言的語法或介面api,這是許多語言所缺乏的,如一個在VC6中編寫的程式碼,拿到Visual Studio2005Visual Studio2008去執行很容易出現問題。

      
也許你並不清楚我在說什麼?但是這一切會在你的Spring學習中漸漸清楚起來!

      
以後的程式碼可能需要必要的IDE才能使用,本人使用的是:
       MyEclipse6.0M1+Eclipse3.3 

       資料庫用的是:
      Oralce10g
或者Mysql6.0

 

 

 

 

 

 

 

相關文章