3. 設計模式-代理模式

本文筆記來自於:狂神的設計模式

代理模式的分類:

  • 靜態代理
  • 動態代理

一、靜態代理

1.1 角色分析:

  • 抽象角色:一般會使用接口或者抽象類來解決
  • 真實角色:被代理的角色(eg:房東)
  • 代理角色:代理真實角色後,我們一般會做一些附屬操作(eg:中介)
  • 客戶:訪問對象的人(eg:租房的人)


1.2 代碼實現

  1. 首先創建抽象角色,將共同業務放入其中

    public interface Rent {
    	void rent();//租房
    }
    
  2. 創建房東,繼承租房接口

    //房東
    public class Host implements Rent{
    	@Override
    	public void rent() {
    		System.out.println("房東租房");
    	}
    }
    
    
  3. 創建中介,繼承租房接口,並擴展業務

    //中介代理
    public class Proxy implements Rent{
    	
    	private Host host;
    	public Proxy(Host host) {
    		this.host = host;
    	}
    	@Override
    	public void rent() {
    		System.out.println("代理幫忙租房!");	
    		host.rent();
    	}
    	public void look() {
    		System.out.println("幫忙看房");
    	}
    	public void agreement() {
    		System.out.println("簽署合同");
    	}
    }
    
  4. 創建客戶

    //客戶
    public class Client {
    	public static void main(String[] args) {
    		Host host = new Host();
    		Proxy proxy = new Proxy(host);
    		proxy.look();
    		proxy.rent();
    		proxy.agreement();
    	}
    }
    

    調用結果如下:

    幫忙看房
    代理幫忙租房!
    房東租房
    簽署合同

代理模式的好處:

● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共業務交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!

缺點:
● 一個真實角色就會產生一個代理角色;代碼量會翻倍開發效率會變低~


1.3 深入理解靜態代理

在現實的項目中,如果我們之前的項目已經寫好了接口(比如增刪改查),那麼我們需要增強功能的時候,不需要修改原來的代碼(在公司中修改原有的代碼是大忌),而是增加一個代理類,達到增強業務的目的。

原有接口和實現方法:

public interface UserService {
	void add();
	void delete();
	void update();
	void query();
}
public class UserServiceImpl implements UserService {

	@Override
	public void add() {
		System.out.println("增加");//執行的操作
	}

	@Override
	public void delete() {
		System.out.println("刪除");
	}

	@Override
	public void update() {
		System.out.println("更新");
	}

	@Override
	public void query() {
		System.out.println("查詢");
	}
}

這時候我們的業務需要在執行每個操作前,輸出日誌,我們不需要去修改UserServiceImpl

的代碼,而是去增加一個代理類UserProxy

public class UserProxy implements UserService{
	
	private UserServiceImpl userServiceImpl;
	
	public void userProxy(UserServiceImpl userServiceImpl) {
		this.userServiceImpl = userServiceImpl;
	}

	@Override
	public void add() {
		System.out.println("[DEBUG]: 調用了add方法");
		userServiceImpl.add();
	}

	@Override
	public void delete() {
		System.out.println("[DEBUG]: 調用了delete方法");
		userServiceImpl.delete();
	}

	@Override
	public void update() {
		System.out.println("[DEBUG]: 調用了update方法");
		userServiceImpl.update();
	}

	@Override
	public void query() {
		System.out.println("[DEBUG]: 調用了query方法");
		userServiceImpl.query();
	}
}

二、動態代理

  • 動態代理和靜態代理角色一樣
  • 動態代理的代理類是動態生成的,不是我們直接寫好的
  • 動態代理分爲兩大類:基於接口的動態代理,基於類的動態代理
    • 基於接口–JDK動態代理
    • 基於類:cglib

需要了解兩個類:Proxy 和 InvocationHandler

2.1 代碼實現

還是以UserService的例子來說:

public interface UserService {
	void add();
	void delete();
	void update();
	void query();
}

public class UserServiceImpl implements UserService {

	@Override
	public void add() {
		System.out.println("增加");
	}

	@Override
	public void delete() {
		System.out.println("刪除");
	}

	@Override
	public void update() {
		System.out.println("更新");
	}

	@Override
	public void query() {
		System.out.println("查詢");
	}
}

不過我們動態代理裏面不需要自己寫代理類了,而是使用一個生成代理類的機制,需要擴展的方法在invoke中調用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理調用處理程序
//會用這個類自動生成代理類
public class ProxyInvocatinHandler implements InvocationHandler{

	//被代理的接口
	private Object in;

	public void setRent(Object in) {
		this.in = in;
	}

	public Object getProxy() {
		 return Proxy.newProxyInstance(this.getClass().getClassLoader(), in.getClass().getInterfaces(), this);
	}
	
	//處理代理實例,並返回結果
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//動態代理的本質,就是使用反射實現的
		log(method.getName());//需要擴展的業務
		Object result = method.invoke(in, args);
		return result;
	}
	
	private void log(String msg) {
		System.out.println("[DEBUG]執行了 "+msg+" 的方法");
	}
}

然後我們使用這個機制,根據被代理的對象自動生成代理類:

public class Client {
	public static void main(String[] args) {
		//真實角色
		Host host = new Host();
		//代理角色
		ProxyInvocatinHandler pih = new ProxyInvocatinHandler();
		//通過調用程序處理角色
		pih.setRent(host);
		//獲取代理
		Rent proxy = (Rent)pih.getProxy();
		proxy.rent();
	}
}

運行結果:

[DEBUG]執行了 add 的方法
增加

應用場景:下次需要在某個方法前打印日誌時,可以直接使用這個動態代理的處理類來代理,不受接口類型的限制,同樣可以運用到房東買房的例子

動態代理的好處:
● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共也就就交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!
● 一個動態代理類代理的是一個接口,一般就是對應的一類業務

	//獲取代理
		Rent proxy = (Rent)pih.getProxy();
		proxy.rent();
	}
}

運行結果:

[DEBUG]執行了 add 的方法
增加

應用場景:下次需要在某個方法前打印日誌時,可以直接使用這個動態代理的處理類來代理,不受接口類型的限制,同樣可以運用到房東買房的例子

動態代理的好處:
● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共也就就交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!
● 一個動態代理類代理的是一個接口,一般就是對應的一類業務

●一個動態代理類可以代理多個類,只要是實現了同一個接口即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章