【菜鳥學Java】12:代理模式——靜態代理怎麼玩?

連江偉發表於2016-02-22

        什麼是靜態代理?

        周所周知,常用的23種設計模式中,有一個代理模式(Proxy Pattern)。它的定義如下:

        為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。

        代理模式的思想是為了提供額外的處理或者不同的操作而在實際物件與呼叫者之間插入一個代理物件。這些額外的操作通常需要與實際物件進行通訊。

        那麼靜態代理又是什麼鬼呢?所謂靜態就是指,在程式執行前代理類和委託類的對應關係就已經確定了,或者說執行前代理類的位元組碼檔案就有了。

        為何用代理模式?

        代理模式主要有三個組成部分:

        抽象角色:通過介面或者抽象類宣告真是角色實現的業務方法。

        代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並且可以附加自己的操作。

        真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫。

        具體的關係我從網上找了一張圖,幫助大家理解:

       

        採用代理模式來開發程式的話,我們的業務類只需要關注自己本身的業務邏輯就行了,其他的什麼日誌,許可權控制等輔助功能,就不用再管了,交給代理類去完成就可以了。這樣保證了業務類的高複用性。

        如何用靜態代理?

        這裡我們使用Java寫一個簡單的demo,來說明如何使用靜態代理。大致需求是一個使用者管理的業務類需要新增日誌列印功能,如何做呢?方案一,直接將日誌列印的程式碼寫到使用者管理的業務邏輯實現類裡面的方法中;方案二,使用靜態代理,將日誌列印的程式碼加到代理類的方法中。在示例程式碼中,兩種方式都有體現。

        使用者管理介面

public interface UserManager {

	public void addUser(String userId, String userName);
	
	public void delUser(String userId);
	
	public void modifyUser(String userId, String userName);
	
	public String findUser(String userId);
}

        使用者管理實現類

public class UserManagerImpl implements UserManager {

	public void addUser(String userId, String userName) {
		//直接在業務實現類的方法中新增列印日誌的程式碼
		//System.out.println("start-->>addUser() userId-->>" + userId);
		try {
			System.out.println("UserManagerImpl.addUser() userId-->>" + userId);
			
			//System.out.println("success-->>addUser()");
		}catch(Exception e) {
			e.printStackTrace();
			//System.out.println("error-->>addUser()");
			throw new RuntimeException();
		}	
	}

	public void delUser(String userId) {
		System.out.println("UserManagerImpl.delUser() userId-->>" + userId);
	}

	public String findUser(String userId) {
		System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
		return "查詢成功,找到使用者張三";
	}

	public void modifyUser(String userId, String userName) {
		System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);
	}

}

        使用者管理代理類

public class UserManagerImplProxy implements UserManager {

	//新增真是物件的引用
	private UserManager userManager;
	//通過構造方法將被代理的物件傳入代理類物件
	public UserManagerImplProxy(UserManager userManager) {
		this.userManager = userManager;
	}

	public void addUser(String userId, String userName) {
		try {
			//為核心業務方法新增列印日誌的功能
			System.out.println("開始新增使用者,呼叫方法:addUser() 使用者ID:userId-->>" + userId);
			userManager.addUser(userId, userName);
			System.out.println("新增使用者成功!");
		}catch(Exception e) {
			e.printStackTrace();
			System.out.println("新增使用者失敗!請聯絡管理員……");
		}	
	}

	public void delUser(String userId) {
		userManager.delUser(userId);
	}

	public String findUser(String userId) {
		return userManager.findUser(userId) ;
	}

	public void modifyUser(String userId, String userName) {
		userManager.modifyUser(userId, userName);
	}

}

        客戶端

public class Client {

	public static void main(String[] args) {
		//UserManager userManager = new UserManagerImpl();
		UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
		userManager.addUser("0001", "張三");
		System.out.println(userManager.findUser("0001"));
	}

}

        小結一下:

        對於靜態代理,有其好處,但是其缺點也是顯而易見的。靜態代理中的代理類和委託類實現了相同的介面,代理類通過委託類實現了相同的方法。這樣就出現了大量的程式碼重複。如果介面新增加一個方法,除了所有的實現類需要實現這個方法外,所有的代理類也需要實現該方法,這就給程式碼的維護增加了很大的麻煩。

        其次,靜態代理模式只服務於一種型別的物件,如果我們要增加一個業務類,那麼就必須相應的增加一個代理類,這樣如果我們的程式規模非常大,使用靜態代理就是一個十分不划算的選擇了。那麼如何解決這個問題呢?且聽下回分解。

相關文章